module Mischief.ECS.Timer where

import Data.Function
import Mischief.ECS.Components
import Mischief.ECS.Components.Bundle
import Mischief.ECS.Time qualified as Time
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.Pipe

data Timer = Timer {Timer -> Float
duration :: Float, Timer -> Float
elapsed :: Float, Timer -> Mode
mode :: Mode}

data Mode = Once | Repeat

new :: Float -> Mode -> Timer
new :: Float -> Mode -> Timer
new Float
duration Mode
mode = Timer {Float
duration :: Float
duration :: Float
duration, Mode
mode :: Mode
mode :: Mode
mode, elapsed :: Float
elapsed = Float
0}

tick :: Float -> Timer -> (Timer, Bool)
tick :: Float -> Timer -> (Timer, Bool)
tick Float
_ Timer
timer | Timer
timer.elapsed Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
>= Timer
timer.duration = (Timer
timer, Bool
False)
tick Float
x Timer
timer | Timer
timer.elapsed Float -> Float -> Float
forall a. Num a => a -> a -> a
+ Float
x Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
< Timer
timer.duration = (Timer
timer {elapsed = timer.elapsed + x}, Bool
False)
tick Float
_ Timer {Float
duration :: Timer -> Float
duration :: Float
duration, elapsed :: Timer -> Float
elapsed = Float
_, mode :: Timer -> Mode
mode = Mode
Once} = (Timer {Float
duration :: Float
duration :: Float
duration, elapsed :: Float
elapsed = Float
duration, mode :: Mode
mode = Mode
Once}, Bool
True)
tick Float
x Timer {Float
duration :: Timer -> Float
duration :: Float
duration, Float
elapsed :: Timer -> Float
elapsed :: Float
elapsed, mode :: Timer -> Mode
mode = Mode
Repeat} = (Timer {Float
duration :: Float
duration :: Float
duration, elapsed :: Float
elapsed = Float
elapsed Float -> Float -> Float
forall a. Num a => a -> a -> a
+ Float
x Float -> Float -> Float
forall a. Num a => a -> a -> a
- Float
duration, mode :: Mode
mode = Mode
Repeat}, Bool
True)

qtimer :: forall b a. (Bundle b, Component b) => (b -> Timer) -> (Timer -> b) -> Query System a -> Query System a
qtimer :: forall b a.
(Bundle b, Component b) =>
(b -> Timer) -> (Timer -> b) -> Query System a -> Query System a
qtimer b -> Timer
f Timer -> b
f' Query System a
x =
  Query System a
x
    Query System a
-> (Query System a -> Query System (a, b)) -> Query System (a, b)
forall a b. a -> (a -> b) -> b
& C b -> (a -> b -> (a, b)) -> Query System a -> Query System (a, b)
forall w (m :: * -> *) qd out a c.
(MonadSystem w m, Queryable qd out) =>
qd -> (a -> out -> c) -> Query m a -> Query m c
qextend (forall a. C a
forall {k} (a :: k). C a
C @b) (,)
    Query System (a, b)
-> (Query System (a, b) -> Query System (a, b))
-> Query System (a, b)
forall a b. a -> (a -> b) -> b
& (Entity -> (a, b) -> System Bool)
-> Query System (a, b) -> Query System (a, b)
forall a (m :: * -> *).
(Entity -> a -> m Bool) -> Query m a -> Query m a
qfilterM
      ( \Entity
entity (a
_, b
a) -> do
          let timer :: Timer
timer = b -> Timer
f b
a
          delta <- System Float
Time.delta
          let (timer', justFinished) = tick delta timer
          insert (f' timer') entity
          pure justFinished
      )
    Query System (a, b)
-> (Query System (a, b) -> Query System a) -> Query System a
forall a b. a -> (a -> b) -> b
& ((a, b) -> a) -> Query System (a, b) -> Query System a
forall w (m :: * -> *) a b.
MonadSystem w m =>
(a -> b) -> Query m a -> Query m b
qmap (a, b) -> a
forall a b. (a, b) -> a
fst

qtimer' :: forall b a. (Bundle b, Component b) => (b -> Timer) -> (Timer -> b) -> Float -> Query System a -> Query System a
qtimer' :: forall b a.
(Bundle b, Component b) =>
(b -> Timer)
-> (Timer -> b) -> Float -> Query System a -> Query System a
qtimer' b -> Timer
f Timer -> b
f' Float
delta Query System a
x =
  Query System a
x
    Query System a
-> (Query System a -> Query System (a, b)) -> Query System (a, b)
forall a b. a -> (a -> b) -> b
& C b -> (a -> b -> (a, b)) -> Query System a -> Query System (a, b)
forall w (m :: * -> *) qd out a c.
(MonadSystem w m, Queryable qd out) =>
qd -> (a -> out -> c) -> Query m a -> Query m c
qextend (forall a. C a
forall {k} (a :: k). C a
C @b) (,)
    Query System (a, b)
-> (Query System (a, b) -> Query System (a, b))
-> Query System (a, b)
forall a b. a -> (a -> b) -> b
& (Entity -> (a, b) -> System Bool)
-> Query System (a, b) -> Query System (a, b)
forall a (m :: * -> *).
(Entity -> a -> m Bool) -> Query m a -> Query m a
qfilterM
      ( \Entity
entity (a
_, b
a) -> do
          let timer :: Timer
timer = b -> Timer
f b
a
          let (Timer
timer', Bool
justFinished) = Float -> Timer -> (Timer, Bool)
tick Float
delta Timer
timer
          b -> Entity -> System ()
forall b. (HasCallStack, Bundle b) => b -> Entity -> System ()
forall b. Bundle b => b -> Entity -> System ()
insert (Timer -> b
f' Timer
timer') Entity
entity
          Bool -> System Bool
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
justFinished
      )
    Query System (a, b)
-> (Query System (a, b) -> Query System a) -> Query System a
forall a b. a -> (a -> b) -> b
& ((a, b) -> a) -> Query System (a, b) -> Query System a
forall w (m :: * -> *) a b.
MonadSystem w m =>
(a -> b) -> Query m a -> Query m b
qmap (a, b) -> a
forall a b. (a, b) -> a
fst