packages feed

swarm-0.4: src/Swarm/Game/Scenario/Topography/Navigation/Waypoint.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
module Swarm.Game.Scenario.Topography.Navigation.Waypoint where

import Data.Int (Int32)
import Data.Text qualified as T
import Data.Yaml as Y
import GHC.Generics (Generic)
import Linear (V2 (..))
import Swarm.Game.Location
import Swarm.Game.Scenario.Topography.Placement

-- | Indicates which structure something came from
-- for debugging purposes.
data Originated a = Originated
  { parent :: Maybe Placement
  , value :: a
  }
  deriving (Show, Eq, Functor)

newtype WaypointName = WaypointName T.Text
  deriving (Show, Eq, Ord, Generic, FromJSON)

-- | Metadata about a waypoint
data WaypointConfig = WaypointConfig
  { wpName :: WaypointName
  , wpUnique :: Bool
  -- ^ Enforce global uniqueness of this waypoint
  }
  deriving (Show, Eq)

parseWaypointConfig :: Object -> Parser WaypointConfig
parseWaypointConfig v =
  WaypointConfig
    <$> v .: "name"
    <*> v .:? "unique" .!= False

instance FromJSON WaypointConfig where
  parseJSON = withObject "Waypoint Config" parseWaypointConfig

-- |
-- A parent world shouldn't have to know the exact layout of a subworld
-- to specify where exactly a portal will deliver a robot to within the subworld.
-- Therefore, we define named waypoints in the subworld and the parent world
-- must reference them by name, rather than by coordinate.
data Waypoint = Waypoint
  { wpConfig :: WaypointConfig
  , wpLoc :: Location
  }
  deriving (Show, Eq)

-- | JSON representation is flattened; all keys are at the same level,
-- in contrast with the underlying record.
instance FromJSON Waypoint where
  parseJSON = withObject "Waypoint" $ \v ->
    Waypoint
      <$> parseWaypointConfig v
      <*> v .: "loc"

-- | Basically "fmap" for the "Location" field
modifyLocation ::
  (Location -> Location) ->
  Waypoint ->
  Waypoint
modifyLocation f (Waypoint cfg originalLoc) = Waypoint cfg $ f originalLoc

-- | Translation by a vector
offsetWaypoint ::
  V2 Int32 ->
  Waypoint ->
  Waypoint
offsetWaypoint locOffset = modifyLocation (.+^ locOffset)