layers-game-0.3: src/GameData/Level.hs
module GameData.Level where
#include "Utils.cpp"
import Control.Applicative ((<$>))
import Data.Function (on)
import qualified Data.List as L
import qualified Data.List.Zipper as LZ
import qualified Gamgine.Utils as GU
import qualified Gamgine.Zipper as GZ
import Gamgine.Control (applyIf)
import qualified Gamgine.Math.Vect as V
import Gamgine.Math.Vect
import qualified Gamgine.Math.Box as B
import qualified Gamgine.Math.BoxTree as BT
import qualified Entity.Bound as EB
import qualified Entity.Id as EI
import qualified GameData.Layer as LY
import qualified GameData.Entity as E
import qualified GameData.Player as PL
import qualified GameData.Boundary as BD
import qualified Rendering.Renderer as RD
IMPORT_LENS_AS_LE
data Level = Level {
boundary :: BD.Boundary,
renderers :: [RD.Renderer],
entities :: [E.Entity],
layers :: LZ.Zipper LY.Layer
}
LENS(boundary)
LENS(renderers)
LENS(entities)
LENS(layers)
instance E.ApplyToEntity Level where
eMap f level = level {entities = E.eMap f $ entities level,
layers = E.eMap f <$> layers level}
eFilter p level = level {entities = E.eFilter p $ entities level,
layers = E.eFilter p <$> layers level}
activeLayerL = activeLayerLens
activeLayerLens = LE.lens getActiveLayer setActiveLayer
where
getActiveLayer = LZ.cursor . layers
setActiveLayer layer = LE.modL layersL $ LZ.replace layer
inactiveLayers :: Level -> [LY.Layer]
inactiveLayers level = before ++ after
where
before = LZ.toList . GZ.before . layers $ level
after = LZ.toList . GZ.after . layers $ level
newLevel :: [E.Entity] -> [LY.Layer] -> Level
newLevel entities layers = Level boundary [] entities $ LZ.fromList layers
where
boundary = BD.newBoundary allEntities
allEntities = entities ++ (L.concat $ L.map LY.entities $ layers)
newEmptyLevel :: Level
newEmptyLevel =
Level {boundary = BD.Boundary $ B.Box V.nullVec (V.v3 2000 1000 0),
renderers = [],
entities = [PL.newPlayer 0 (V.v3 1 1 0)],
layers = LZ.fromList [LY.newEmptyLayer, LY.newEmptyLayer]}
changeLevels :: Level -> Level -> (Level, Level)
changeLevels fromLevel toLevel =
case findEntity E.isPlayer fromLevel of
Just E.Player {E.playerVelocity = (vx:._)} ->
(fromLevel, E.eMap (applyIf E.isPlayer $ \p -> p {E.playerVelocity = (vx:.0:.0)}) toLevel)
_ -> (fromLevel, toLevel)
allLayers :: Level -> [LY.Layer]
allLayers = LZ.toList . layers
toNextLayer :: Level -> Level
toNextLayer = LE.modL layersL $ \lays -> applyIf LZ.endp LZ.start $ LZ.right lays
-- | the entities of the level and all its layers
allEntities :: Level -> [E.Entity]
allEntities Level {entities = entities, layers = layers} =
entities ++ (L.concat $ L.map LY.entities $ LZ.toList layers)
findEntity :: (E.Entity -> Bool) -> Level -> Maybe E.Entity
findEntity f level = L.find f $ allEntities level
findEntityById id = findEntity $ (== id) . EI.entityId
findEntityAt pos = findEntity $ \e -> (BT.asBox . EB.bound $ e) `B.contains` pos
data AddEntityTo = ToLevel | ToActiveLayer
addEntity :: E.Entity -> AddEntityTo -> Level -> Level
addEntity entity ToLevel = LE.modL entitiesL (entity :)
addEntity entity ToActiveLayer = LE.modL (LY.entitiesL . activeLayerL) (entity :)
freeEntityId :: Level -> Int
freeEntityId level = maxId + 1
where
maxId | L.null allIds = 0
| otherwise = L.maximum allIds
allIds = L.map EI.entityId (allEntities level)
allStarsCollected :: Level -> Bool
allStarsCollected = L.all (== True) . L.map E.starCollected . L.filter E.isStar . allEntities
-- | a lens for the player entity
playerL = playerLens
playerLens = LE.lens getPlayer setPlayer
where
getPlayer = \level ->
case findEntity E.isPlayer level of
Just p -> p
_ -> error $ "Couldn't find player!"
setPlayer = \player level -> E.eMap (set player) level
where
set player E.Player {} = player
set _ e = e