abradley2/elm-game-logic/1.2.0

NOTE: the latest version is 1.2.1

Logic.System

System: main logic driver, that is used to stepping on each game-loop and update World

type alias System world =
world -> world
update : Spec comp world -> (Set comp -> Set comp) -> world -> world

Update whole Component.Set

step : (comp -> comp) -> Spec comp world -> world -> world

Single component mapping, Same asList.map - only for Component.Set inside World

gravitySystem =
    Logic.System.step (Vec2.add gravity) accelerationSpec
( ( a, a -> System (Acc2 a b) )
-> ( b, b -> System (Acc2 a b) )
-> System (Acc2 a b)
)
-> Spec a world
-> Spec b world
-> world
-> world

Step over all entities that have both components.

Example:

system =
    Logic.System.step2
        (\( v, _ ) ( p, setP ) -> setP (Vec2.add v p))
        velocitySpec
        positionSpec
( ( a, a -> System (Acc3 a b c) )
-> ( b, b -> System (Acc3 a b c) )
-> ( c, c -> System (Acc3 a b c) )
-> System (Acc3 a b c)
)
-> Spec a world
-> Spec b world
-> Spec c world
-> world
-> world

Same as step2 only with 3 components

( ( a, a -> System (Acc4 a b c d) )
-> ( b, b -> System (Acc4 a b c d) )
-> ( c, c -> System (Acc4 a b c d) )
-> ( d, d -> System (Acc4 a b c d) )
-> System (Acc4 a b c d)
)
-> Spec a world
-> Spec b world
-> Spec c world
-> Spec d world
-> world
-> world

Same as step2 only with 4 components

( ( a, a -> System (Acc5 a b c d e) )
-> ( b, b -> System (Acc5 a b c d e) )
-> ( c, c -> System (Acc5 a b c d e) )
-> ( d, d -> System (Acc5 a b c d e) )
-> ( e, e -> System (Acc5 a b c d e) )
-> System (Acc5 a b c d e)
)
-> Spec a world
-> Spec b world
-> Spec c world
-> Spec d world
-> Spec e world
-> world
-> world

Same as step2 only with 5 components

foldl : (comp1 -> acc -> acc) -> Set comp1 -> acc -> acc

Reduce a Component.Set from the left.

Example count how much enemies left in the world:

enemySet =
    enemySpec.get world


count =
    foldl (\_ -> (+) 1) enemySet 0
foldl2 : (comp1 -> comp2 -> acc -> acc) -> Set comp1 -> Set comp2 -> acc -> acc

Step over all entities that have both components and reduce an Component.Sets from the left.

(comp1 -> comp2 -> comp3 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> acc
-> acc

Same as foldl2 only with 3 components

(comp1 -> comp2 -> comp3 -> comp4 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> Set comp4
-> acc
-> acc

Same as foldl2 only with 4 components

(comp1 -> comp2 -> comp3 -> comp4 -> comp5 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> Set comp4
-> Set comp5
-> acc
-> acc

Same as foldl2 only with 5 components

indexedFoldl : (EntityID -> comp1 -> acc -> acc) -> Set comp1 -> acc -> acc

Variant of foldl that passes the index of the current element to the step function.

indexedFoldl is to foldl as List.indexedMap is to List.map.

(EntityID -> comp1 -> comp2 -> acc -> acc)
-> Set comp1
-> Set comp2
-> acc
-> acc
(EntityID -> comp1 -> comp2 -> comp3 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> acc
-> acc

Same as indexedFoldl2 only with 3 components

(EntityID -> comp1 -> comp2 -> comp3 -> comp4 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> Set comp4
-> acc
-> acc

Same as indexedFoldl2 only with 4 components

(EntityID -> comp1 -> comp2 -> comp3 -> comp4 -> comp5 -> acc -> acc)
-> Set comp1
-> Set comp2
-> Set comp3
-> Set comp4
-> Set comp5
-> acc
-> acc

Same as indexedFoldl2 only with 5 components

Advanced Folds

isOptional : Set a -> FoldSpec a (Maybe a)

Specify that a component to be used in an advanced fold operation is optional.

isRequired : Set a -> FoldSpec a a

Specify that a component to be used in an advanced fold operation is required.

isProhibited : Set a -> FoldSpec a ()

Specify that an entityId an advanced fold operation must not have this component

(EntityID -> a2 -> b2 -> acc -> acc)
-> FoldSpec a1 a2
-> FoldSpec b1 b2
-> acc
-> acc

similar to indexFoldl2, but allows you to specify which components are required and which are optional.

The tests are a good example of how this works

let
    world =
        withEntities
            [ -- entity 1
              [ ( fooSpec, 1 ) ]


            -- entity 2
            , [ ( fooSpec, 2 ), ( barSpec, 3 ) ]


            -- entity 3
            , [ ( barSpec, 999 ) ]
            ]


    result =
        Logic.System.advancedFoldl2
            (\_ foo barMaybe acc -> acc + foo + Maybe.withDefault 0 barMaybe)
            (Logic.System.isRequired world.foos)
            (Logic.System.isOptional world.bars)
            0
in
Expect.equal result 6
(EntityID -> a2 -> b2 -> c2 -> acc -> acc)
-> FoldSpec a1 a2
-> FoldSpec b1 b2
-> FoldSpec c1 c2
-> acc
-> acc

a version of advancedFoldl2 that allows you to fold over 3 components

(EntityID -> a2 -> b2 -> c2 -> d2 -> acc -> acc)
-> FoldSpec a1 a2
-> FoldSpec b1 b2
-> FoldSpec c1 c2
-> FoldSpec d1 d2
-> acc
-> acc

a version of advancedFoldl2 that allows you to fold over 4 components

(EntityID -> a2 -> b2 -> c2 -> d2 -> e2 -> acc -> acc)
-> FoldSpec a1 a2
-> FoldSpec b1 b2
-> FoldSpec c1 c2
-> FoldSpec d1 d2
-> FoldSpec e1 e2
-> acc
-> acc

a version of advancedFoldl2 that allows you to fold over 5 components

Util

applyIf : Bool -> (a -> a) -> a -> a

Just nice helper function to pipe into systems

update msg world =
    world
        |> system1
        |> applyIf (msg === KeyUp "a") systemMoveLeft
        |> system2
applyMaybe : Maybe a -> (a -> c -> c) -> c -> c

Same as applyIf, but works with Maybe

update msg world =
    world
        |> system1
        |> applyMaybe (decode saveDecoder msg) loadGame
        |> system2