packages feed

mischief-ecs-0.1.0.0: src/Mischief/ECS/World/Modify.hs

module Mischief.ECS.World.Modify where

import Data.Maybe
import Mischief.ECS.Components
import Mischief.ECS.Components.Bundle
import Mischief.ECS.Components.Common
import Mischief.ECS.Entities
import Mischief.ECS.Log
import Mischief.ECS.Tables
import Mischief.ECS.World
import Mischief.ECS.World.Insert
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers
import Mischief.ECS.World.Query.Queryable
import Mischief.ECS.World.Remove

-- | Modifies the value of the component with the given function.
--
-- The function will be applied over the @live value@ of the component, adding some overhead.
--
-- If you are confident the value in the 'Result' is the live one, or otherwise do not care of updating the live value,
-- you are encouraged to use 'modify'' instead.
--
-- Note that this will trigger change detection even if the provided function is 'id'.
modify :: forall c i. (Updateable (Result c), Settable (Result c) i, DeepValue (Result c) i) => Result c -> (i -> i) -> System ()
modify !result !f = do
  res <- update result
  case res of
    Nothing -> warn $ "Modify failed: Entity " <> text (entityOf result) <> " is not alive."
    Just res -> do
      let v = deepValue res
      set result (f v)

-- | Modifies the value of the component with the given function.
--
-- The function will be applied over the value contained within the 'Result', which is not guaranteed
-- to be the live value of the component.
--
-- If you wish to apply the function over the live value, use 'modify' instead.
--
-- Note that this will trigger change detection even if the provided function is 'id'.
modify' :: forall c i. (Settable (Result c) i, DeepValue (Result c) i) => Result c -> (i -> i) -> System ()
modify' !result !f = do
  let v = deepValue result
  set result (f v)

-- | The most generic function for modifying a component on a given entity.
--
-- This can do removals, insertions, and modify the value.
--
-- Example:
--
-- @
-- data Counter = Counter 'Int' deriving ('Component', 'Queryable')
--
-- incrementCounter :: 'Entity' -> 'System' ()
-- incrementCounter = 'alter' (\case 'Nothing' -> 'Just' $ Counter 0; 'Just' (Counter x) -> 'Just' $ Counter (x + 1))
-- @
alter :: forall c. (Queryable (C c) (Result c), Bundle c, Component c) => (Maybe c -> Maybe c) -> Entity -> System ()
alter !f !entity = do
  val <- get (C @c) entity
  let r = f (fmap value val)

  if isNothing r && isJust val
    then
      remove (C @c) entity
    else case r of
      Just r ->
        insert r entity
      Nothing -> return ()