packages feed

ohhecs-0.0.2: src/Games/ECS/Prototype/SpawnedFromPrototype.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE Trustworthy #-}
-- |
-- Module      :  Games.ECS.Prototype.SpawnedFromPrototype
-- Description : Prototype definitions
-- Copyright   :  (C) 2020 Sophie Taylor
-- License     :  AGPL-3.0-or-later
-- Maintainer  :  Sophie Taylor <sophie@spacekitteh.moe>
-- Stability   :  experimental
-- Portability: GHC
--
-- A component to mark an entity as being spawned from a prototype.
module Games.ECS.Prototype.SpawnedFromPrototype where

import Control.Exception.Assert.Sugar      
import Games.ECS.World
import Control.Monad.IO.Class

import Games.ECS.Prototype    

import Games.ECS.Serialisation
import Games.ECS.Entity
import Games.ECS.Component
import Games.ECS.Component.TH
import GHC.Generics
import Control.Lens    

-- | Marks an entity as being spawned from a prototype.
data SpawnedFromPrototype = SpawnedFromPrototype
  { -- | The raw `Entity ` which it is spawned from.
    _prototypeEntity :: !Entity,
    -- | The t`PrototypeID` it is spawned from.
    _spawnedFromPrototypeID :: Maybe PrototypeID
  }
  deriving stock (Eq, Generic, Show)

makeLenses ''SpawnedFromPrototype

instance {-# OVERLAPS #-} XMLPickler [Node] SpawnedFromPrototype where
  {-# INLINE xpickle #-}
  xpickle =
    xpWrap
      (\(pEnt, pid) -> SpawnedFromPrototype pEnt pid)
      (\(SpawnedFromPrototype pEnt pid) -> (pEnt, pid))
      ( xpElemAttrs
          "spawnedFromPrototype"
          ( xpPair
              (pickleAsAttribute "entRef")
              (pickleAsAttribute "prototypeID")
          )
      )


    
instance Component SpawnedFromPrototype where
  type CanonicalName SpawnedFromPrototype = "spawnedFromPrototype"

makeHasComponentClass ''SpawnedFromPrototype

{-# INLINEABLE spawnPrototype #-}

-- | Spawn a new individual with the given prototype `Entity` reference. Returns the new individual, and the new world.
spawnPrototype :: (UsingSpawnedFromPrototype w Individual, UsingIsPrototype w Individual, MonadIO m) => Entity -> w Storing -> m (Maybe (w Individual, w Storing))
spawnPrototype proto world = do
  let protoCritter = lookupEntity world proto
  case protoCritter of
    Nothing -> pure Nothing
    Just critter -> do
      let protoID = critter ^? isPrototype . prototypeID
      newID <- liftIO newUniqueEntRef
      let newCritter = critter & unsafeEntityReference .~ newID & addSpawnedFromPrototype .~ SpawnedFromPrototype proto protoID & removeIsPrototype
          updatedWorld = world & storeEntity newCritter
      pure (Just (newCritter, updatedWorld))

-- | Spawns a new individual with a given t`PrototypeID`, which is looked up in the associated map. Returns the new individual, and the new world.
{-# INLINEABLE spawnNamedPrototype #-}
spawnNamedPrototype :: (UsingSpawnedFromPrototype w Individual, UsingIsPrototype w Individual,MonadIO m) => PrototypeID -> w Storing -> m (Maybe (w Individual, w Storing))
spawnNamedPrototype prototypeName world = do
  let proto = world ^? prototype prototypeName . entityReference
  case proto of
    Nothing -> assert (False `blame` "Prototype ID doesn't exist!" `swith` proto) (pure Nothing)
    Just ent -> do
      spawned <- spawnPrototype ent world
      case spawned of
        Nothing -> assert (False `blame` ("Prototype ID " ++ show proto ++ " existed, but the entity it refers to doesn't!") `swith` ent) (pure Nothing)
        Just m -> pure (Just m)

-- | All the prototypical individuals in a world.
{-# INLINE prototypes #-}
prototypes :: (HasIsPrototype w) => IndexedTraversal' Entity (w Storing) (w Individual)
prototypes = entitiesWith withIsPrototype