packages feed

aztecs-0.7.0: src/Aztecs/ECS/Query/Dynamic.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TypeFamilies #-}

module Aztecs.ECS.Query.Dynamic
  ( -- * Dynamic queries
    DynamicQuery (..),
    ArrowDynamicQueryReader (..),
    ArrowDynamicQuery (..),
    fromDynReader,

    -- * Dynamic query filters
    DynamicQueryFilter (..),
  )
where

import Aztecs.ECS.Entity (EntityID)
import Aztecs.ECS.Query.Dynamic.Class (ArrowDynamicQuery (..))
import Aztecs.ECS.Query.Dynamic.Reader (DynamicQueryFilter (..), DynamicQueryReader (..))
import Aztecs.ECS.Query.Dynamic.Reader.Class (ArrowDynamicQueryReader (..))
import Aztecs.ECS.World.Archetype (Archetype)
import qualified Aztecs.ECS.World.Archetype as A
import Control.Arrow (Arrow (..), ArrowChoice (..))
import Control.Category (Category (..))
import Data.Either (partitionEithers)
import Prelude hiding ((.))

-- | Dynamic query for components by ID.
newtype DynamicQuery i o
  = DynamicQuery {dynQueryAll :: [i] -> [EntityID] -> Archetype -> ([o], Archetype)}
  deriving (Functor)

instance Applicative (DynamicQuery i) where
  pure a = DynamicQuery $ \_ es arch -> (replicate (length es) a, arch)

  f <*> g = DynamicQuery $ \i es arch ->
    let (as, arch') = dynQueryAll g i es arch
        (fs, arch'') = dynQueryAll f i es arch'
     in (zipWith ($) fs as, arch'')

instance Category DynamicQuery where
  id = DynamicQuery $ \as _ arch -> (as, arch)

  f . g = DynamicQuery $ \i es arch ->
    let (as, arch') = dynQueryAll g i es arch in dynQueryAll f as es arch'

instance Arrow DynamicQuery where
  arr f = DynamicQuery $ \bs _ arch -> (fmap f bs, arch)
  first f = DynamicQuery $ \bds es arch ->
    let (bs, ds) = unzip bds
        (cs, arch') = dynQueryAll f bs es arch
     in (zip cs ds, arch')

instance ArrowChoice DynamicQuery where
  left f = DynamicQuery $ \eds es arch ->
    let (es', ds) = partitionEithers eds
        (cs, arch') = dynQueryAll f es' es arch
     in (fmap Left cs ++ fmap Right ds, arch')

instance ArrowDynamicQueryReader DynamicQuery where
  entity = fromDynReader entity
  fetchDyn = fromDynReader . fetchDyn
  fetchMaybeDyn = fromDynReader . fetchMaybeDyn

instance ArrowDynamicQuery DynamicQuery where
  setDyn cId = DynamicQuery $ \is es arch ->
    let !arch' = A.insertAscList cId (zip es is) arch in (is, arch')

fromDynReader :: DynamicQueryReader i o -> DynamicQuery i o
fromDynReader q = DynamicQuery $ \is es arch ->
  let os = dynQueryReaderAll q is es arch in (os, arch)