packages feed

aztecs-0.1.0.1: src/Data/Aztecs/World/Components.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Data.Aztecs.World.Components
  ( Component (..),
    Components,
    union,
    spawn,
    insert,
    adjust,
    get,
    getRow,
    newComponents,
    setRow,
    remove,
  )
where

import Data.Aztecs.Core
import Data.Aztecs.Storage (Storage, table)
import qualified Data.Aztecs.Storage as S
import Data.Dynamic (Dynamic, fromDynamic, toDyn)
import Data.Map (Map, alter, empty, lookup)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Typeable
import Prelude hiding (read)

class (Typeable a) => Component a where
  storage :: Storage a
  storage = table

data Components = Components (Map TypeRep Dynamic) Entity deriving (Show)

newComponents :: Components
newComponents = Components empty (Entity 0)

union :: Components -> Components -> Components
union (Components a e) (Components b _) = Components (Map.union a b) e

spawn :: forall c. (Component c) => c -> Components -> IO (Entity, Components)
spawn c (Components w (Entity e)) = do
  w' <- insert (Entity e) c (Components w (Entity $ e + 1))
  return (Entity e, w')

insert :: forall c. (Component c) => Entity -> c -> Components -> IO Components
insert e c (Components w e') = do
  w' <-
    Map.alterF
      ( \maybeRow -> do
          s <- S.spawn (fromMaybe storage (maybeRow >>= fromDynamic)) e c
          return . Just $ toDyn s
      )
      (typeOf (Proxy :: Proxy c))
      w
  return $ Components w' e'

adjust :: (Component c) => c -> (c -> c) -> Entity -> Components -> IO Components
adjust a f w = insert w (f a)

getRow :: (Component c) => Proxy c -> Components -> Maybe (Storage c)
getRow p (Components w _) = Data.Map.lookup (typeOf p) w >>= fromDynamic

get :: forall c. (Component c) => Entity -> Components -> IO (Maybe (c, c -> Components -> IO Components))
get e (Components w _) = case Data.Map.lookup (typeOf @(Proxy c) Proxy) w >>= fromDynamic of
  Just s -> do
    res <- S.get s e
    case res of
      Just (c, f) ->
        return $
          Just
            ( c,
              \c' (Components w' e') ->
                return $ Components (alter (\row -> Just . toDyn $ f c' (fromMaybe storage (row >>= fromDynamic))) (typeOf @(Proxy c) Proxy) w') e'
            )
      Nothing -> return Nothing
  Nothing -> return Nothing

setRow :: forall c. (Component c) => Storage c -> Components -> Components
setRow cs (Components w e') = Components (Map.insert (typeOf @(Proxy c) Proxy) (toDyn cs) w) e'

remove :: forall c. (Component c) => Entity -> Components -> Components
remove e (Components w e') = Components (alter (\row -> row >>= f) (typeOf @(Proxy c) Proxy) w) e'
  where
    f row = fmap (\row' -> toDyn $ S.remove @c row' e) (fromDynamic row)