packages feed

mischief-ecs-0.2.0.0: src/Mischief/ECS/World/Change.hs

module Mischief.ECS.World.Change where

import Control.Monad.Reader (MonadIO (liftIO))
import Data.Foldable
import Data.IORef
import Data.Maybe
import Data.Set qualified as Set
import Data.Text qualified as T
import GHC.Base (Int (..))
import GHC.Stack (HasCallStack)
import Mischief.ECS.App.SystemDef
import Mischief.ECS.Archetypes
import Mischief.ECS.Components
import Mischief.ECS.Components.Bundle
import Mischief.ECS.Entities
import Mischief.ECS.Log
import Mischief.ECS.Tables
import Mischief.ECS.Vec qualified as Vec
import Mischief.ECS.World
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers
import Mischief.ECS.World.Utils

data ChangeResult = ChangeResult
  { requiredComponentsAdded :: [ProcessedBundleElement],
    newComponents :: [ProcessedBundleElement]
  }

changeArchetype :: (HasCallStack) => Entity -> ArchetypeData -> Maybe ProcessedBundleData -> System ChangeResult
changeArchetype !entity !newArchetype !insertedBundle = do
  !world <- unsafeGetWorld
  !currentTick <- liftIO $ readIORef world.tick

  !reqAdded <- liftIO $ newIORef []
  !newAdded <- liftIO $ newIORef []

  Just !pointer <- liftIO $ getPointer entity world.entities
  (EntityPointer (# !archetypeId', !rowIndex' #)) <- liftIO $ readIORef pointer

  !table <- Vec.read world.tables.inner (I# archetypeId')

  !collected <- liftIO $ takeComponentsFromTable (EntityPointer (# archetypeId', rowIndex' #)) table

  -- TODO OPT: this whole thing can be greatly optimized
  !newElements'' <-
    mapM
      ( \(!component) -> do
          case insertedBundle of
            Nothing -> maybe undefined return (find (\(!x) -> x.id == component) collected.elements)
            Just !bundle ->
              case find (\(!x) -> x.id == component) bundle.elements of
                Just !x -> do
                  case find (\(!x) -> x.id == component) collected.elements of
                    Nothing -> do
                      liftIO $ modifyIORef' newAdded (++ [x])
                    Just _ -> pure ()
                  pure x
                Nothing ->
                  case find (\(!x) -> x.id == component) collected.elements of
                    Just !x -> return x
                    Nothing -> do
                      !r <- getDefault component
                      liftIO $ modifyIORef' reqAdded (++ [r])
                      pure r
      )
      $ Set.toList newArchetype.components

  -- let newElements = case insertedBundle of
  --       Nothing -> ProcessedBundleData newElements''
  --       Just bundle ->
  --         let !newElements' = setChangedTickOfComponents (ProcessedBundleData newElements'') (\(!id) -> isInProcessedBundle bundle id && isInProcessedBundle collected id) currentTick
  --          in setAddedTickOfComponents newElements' (\(!id) -> isInProcessedBundle bundle id && not (isInProcessedBundle collected id)) currentTick

  let newElements = ProcessedBundleData newElements''

  !newComponents <- liftIO $ readIORef newAdded
  let changed = maybe [] ((map (.id)) . (.elements)) insertedBundle ++ map (.id) newComponents

  -- warn $ T.show newArchetype.id
  -- warn $ T.show $ changed

  liftIO $ insertEntityIntoTables newElements currentTick changed world.tables newArchetype.id (entity, pointer)

  !requiredComponentsAdded <- liftIO $ readIORef reqAdded
  pure ChangeResult {requiredComponentsAdded, newComponents}

getDefault :: ComponentId -> System ProcessedBundleElement
getDefault (ComponentId (# !id, !e #)) = do
  !world <- unsafeGetWorld

  Just !x <- single $ mkGet (Entity (# id, 0## #)) (C @DefaultValue)
  let (DefaultValue !value) = x

  let (SystemId !sys) = world.systemId
  !currentSystemTick <- fromMaybe (SystemTick $ Tick 0) <$> single (mkGet sys (C @SystemTick))

  pure
    ProcessedBundleElement
      { id = ComponentId (# id, e #),
        component = value
      }