mischief-ecs-0.1.0.0: src/Mischief/ECS/World/Remove.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
module Mischief.ECS.World.Remove (remove, delete, removeRel, triggerRemoveEvent) where
import Control.Monad
import Control.Monad.Reader
import Data.Data
import Data.Foldable
import Data.IORef
import Data.Map qualified as Map
import Data.Maybe
import Data.Set (Set)
import Data.Set qualified as Set
import GHC.Base (Int (..), eqWord#, isTrue#)
import Mischief.ECS.Archetypes
import Mischief.ECS.Archetypes.Graph
import Mischief.ECS.Collectable
import Mischief.ECS.Components
import Mischief.ECS.Components.Common
import Mischief.ECS.Components.Spawn
import Mischief.ECS.Entities
import Mischief.ECS.EntityDef
import Mischief.ECS.EventDef
import Mischief.ECS.Events
import Mischief.ECS.Log
import Mischief.ECS.Tables
import Mischief.ECS.World
import Mischief.ECS.World.Change (changeArchetype)
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers
import Mischief.ECS.World.Query.Queryable
import Mischief.ECS.World.Utils
newtype ToRemove = ToRemove {inner :: [(ComponentType, Maybe Entity, Maybe Any)]} deriving newtype (Semigroup)
instance (Component c) => EraseIntoStorage (C c) ToRemove where
erase _ = ToRemove [(ComponentType $ Proxy @c, Nothing, Nothing)]
instance (Component c) => EraseIntoStorage (R c Entity) ToRemove where
erase (R e) = ToRemove [(ComponentType $ Proxy @c, Just e, Nothing)]
instance (Component c) => EraseIntoStorage (R c Any) ToRemove where
erase _ = ToRemove [(ComponentType $ Proxy @c, Nothing, Just Any)]
-- remove :: forall r. (Removable r) => Entity -> System ()
-- remove entity = do
-- types <- getTypes (Proxy @r)
-- removeFromEntity (Set.toList types) entity
class Delete r where
delete :: r -> System ()
class Delete' r isRel where
delete' :: r -> System ()
instance (Delete' (Result r) (IsComp r)) => Delete (Result r) where
delete = delete' @(Result r) @(IsComp r)
instance (Component c) => Delete' (Result c) True where
delete' :: Result c -> System ()
delete' result = remove (C @c) (entityOf result)
instance (Component c) => Delete' (Result (Rel c)) False where
delete' :: Result (Rel c) -> System ()
delete' result = remove (R @c result.target) (entityOf result)
remove :: (Collectable c ToRemove) => c -> Entity -> System ()
remove c entity = do
let list :: ToRemove = collect c
for_ list.inner $ \case
(x, Nothing, Nothing) -> do
comp <- getOrAddComponentId x
removeFromEntity [comp] entity
(x, Just target, _) -> do
comp <- getOrAddPairId (Pair (x, target))
removeFromEntity [comp] entity
(x, _, Just _) -> do
removeRelationshipsFromEntity x entity
removeRel :: forall c. (Component c) => Entity -> Entity -> System ()
removeRel = removeRelationshipFromEntity @c
removeRelationshipFromEntity :: forall c. (Component c) => Entity -> Entity -> System ()
removeRelationshipFromEntity target entity = do
componentId <- getOrAddPairId (Pair (ComponentType $ Proxy @c, target))
removeFromEntity [componentId] entity
removeRelationshipsFromEntity :: ComponentType -> Entity -> System ()
removeRelationshipsFromEntity x entity = do
world <- unsafeGetWorld
ids <- liftIO $ findComponentsOfEntity world entity
(ComponentId (# id, _ #)) <- getOrAddComponentId x
for_ ids $ \ids' -> do
let ids = filter (\(ComponentId (# id', _ #)) -> isTrue# $ eqWord# id id') ids'
removeFromEntity ids entity
removeFromEntity :: [ComponentId] -> Entity -> System ()
removeFromEntity components entity = do
world <- unsafeGetWorld
pointer <- liftIO $ getPointer entity world.entities
case pointer of
Nothing -> warn $ "Removal failed: Entity " <> text entity <> " is not alive."
Just pointer -> do
(EntityPointer (# archetypeId, _ #)) <- liftIO $ readIORef pointer
(newArchetype, removedComponents) <- getArchetypeOnRemove (ArchetypeId $ I# archetypeId) components
triggerRemoveEvent removedComponents entity
void $ changeArchetype entity newArchetype Nothing
triggerRemoveEvent :: [ComponentId] -> Entity -> System ()
triggerRemoveEvent components entity = do
for_ components $ \(ComponentId (# id, target #)) -> do
Just t <- get (C @ComponentType) (Entity (# id, 0## #))
case target of
Nothing -> triggerRemoveEventC (value t) entity
Just target -> triggerRemoveEventR (value t) target entity
triggerRemoveEventC :: ComponentType -> Entity -> System ()
triggerRemoveEventC (ComponentType (_ :: Proxy t)) entity =
runEvent $ eraseEvent $ OnRemove @t entity
triggerRemoveEventR :: ComponentType -> Entity -> Entity -> System ()
triggerRemoveEventR (ComponentType (_ :: Proxy t)) target entity =
runEvent $ eraseEvent $ OnRemoveRel @t entity target