291 lines
9.1 KiB
Haskell
291 lines
9.1 KiB
Haskell
{- |
|
|
Module : Dodge.Update
|
|
Description : Simulation update
|
|
-}
|
|
module Dodge.Update
|
|
( update
|
|
) where
|
|
import Dodge.Data
|
|
import Dodge.Menu
|
|
import Dodge.Zone.Data
|
|
import Dodge.Config.Data
|
|
import Dodge.World.Trigger.Data
|
|
--import Dodge.Config.Data
|
|
import Dodge.Base
|
|
import Dodge.Zone
|
|
import Dodge.WallCreatureCollisions
|
|
import Dodge.LevelGen.Block
|
|
import Dodge.Update.Camera
|
|
--import Dodge.Update.UsingInput
|
|
import Dodge.SoundLogic
|
|
import Dodge.Inventory
|
|
import Dodge.Initialisation
|
|
import Dodge.Layout
|
|
import Dodge.Floor
|
|
import Geometry
|
|
import Geometry.Vector3D
|
|
|
|
import Data.List
|
|
import Data.Maybe
|
|
import Data.Function
|
|
import qualified Data.Set as S
|
|
import qualified Data.IntMap.Lazy as IM
|
|
import qualified Data.Map as M
|
|
import Control.Lens
|
|
import Data.Monoid
|
|
import System.Random
|
|
--import Data.Bifunctor
|
|
|
|
update :: World -> World
|
|
update = (frameClock +~ 1) . functionalUpdate
|
|
|
|
{- |
|
|
The update step.
|
|
For most menus the only way to change the world is using event handling. -}
|
|
functionalUpdate :: World -> World
|
|
functionalUpdate w = case _menuLayers w of
|
|
(TerminalScreen t xs:mls) -> w & menuLayers .~ (TerminalScreen (max (t-1) 0) xs:mls)
|
|
(WaitScreen s i : _)
|
|
| i < 1 -> w & dbArg _worldEvents
|
|
| otherwise -> w & menuLayers %~ ( (WaitScreen s (i-1) :) . tail )
|
|
(OptionScreen {_scOptionFlag = GameOverOptions} : _) -> updateParticles
|
|
. updateProjectiles
|
|
. updateLightSources
|
|
. updateClouds
|
|
$ updateSoundQueue
|
|
w
|
|
(_ : _) -> w
|
|
[] -> checkEndGame
|
|
. updateRadialDistortions
|
|
. updateTriggers
|
|
. ppEvents
|
|
. updateCamera
|
|
. colCrsWalls
|
|
. ifConfigWallRotate
|
|
. simpleCrSprings
|
|
. zoneCreatures
|
|
. updateWalls
|
|
. resetWorldEvents
|
|
. dbArg _worldEvents
|
|
. updateParticles
|
|
. updateProjectiles
|
|
. updateLightSources
|
|
. updateClouds
|
|
. zoneClouds
|
|
. updateCreatures
|
|
. updateCreatureGroups
|
|
. updateBlocks
|
|
. updateSeenWalls
|
|
. updateSoundQueue
|
|
$ updateCloseObjects w
|
|
where
|
|
zoneCreatures = set (creaturesZone . znObjects)
|
|
(IM.foldl' (flip creatureInZone) IM.empty (_creatures w))
|
|
creatureInZone cr = insertIMInZone x y cid cr
|
|
where
|
|
(x,y) = crZoneOfPoint $ _crPos cr
|
|
cid = _crID cr
|
|
zoneClouds = set (cloudsZone . znObjects) (foldl' (flip cloudInZone) IM.empty (_clouds w))
|
|
cloudInZone cl = insertInZoneWith x y (++) [cl]
|
|
where
|
|
(x,y) = cloudZoneOfPoint $ stripZ $ _clPos cl
|
|
|
|
-- | Note the explict use of record syntax. Using lens creates a space leak.
|
|
resetWorldEvents :: World -> World
|
|
resetWorldEvents w = w {_worldEvents = id}
|
|
|
|
updateCreatureGroups :: World -> World
|
|
updateCreatureGroups w = w & creatureGroups %~
|
|
IM.mapMaybe (\cgp -> _crGroupUpdate cgp w cgp)
|
|
|
|
updateRadialDistortions :: World -> World
|
|
updateRadialDistortions = radDistortion %~ mapMaybe decreaseBulge
|
|
|
|
decreaseBulge
|
|
:: (Point2,Point2,Point2,Float)
|
|
-> Maybe (Point2,Point2,Point2,Float)
|
|
decreaseBulge (a,b,c,v)
|
|
| v > 1 = Just (a,b,c,max 1 (v - 0.05) )
|
|
| v < 1 = Just (a,b,c,min 1 (v + 0.05) )
|
|
| otherwise = Nothing
|
|
|
|
updateTriggers :: World -> World
|
|
updateTriggers w
|
|
| ResetLevel 1 `S.member` _worldTriggers w = generateLevelFromRoomList levx $ initialWorld
|
|
& randGen .~ _randGen w
|
|
& config .~ _config w
|
|
& menuLayers .~ []
|
|
& creatures . ix 0 .~ cr
|
|
| otherwise = w
|
|
where
|
|
cr = _creatures w IM.! 0 & crPos .~ V2 0 0
|
|
|
|
updateSoundQueue :: World -> World
|
|
updateSoundQueue = set sounds M.empty
|
|
|
|
updateLightSources :: World -> World
|
|
updateLightSources w = set tempLightSources (catMaybes tlss) w'
|
|
where
|
|
(w',tlss) = mapAccumR (\a b -> _tlsUpdate b a b) w $ _tempLightSources w
|
|
|
|
updateProjectiles :: World -> World
|
|
updateProjectiles w = IM.foldl' (flip $ dbArg _pjUpdate) w $ _projectiles w
|
|
{-
|
|
Apply internal particle updates, delete 'Nothing's. -}
|
|
updateParticles :: World -> World
|
|
updateParticles w = set particles (catMaybes ps) w'
|
|
where
|
|
(w',ps) = mapAccumR (\a b -> _ptUpdate' b a b) w $ _particles w
|
|
|
|
-- Note that this updates the randgen
|
|
updateCreatures :: World -> World
|
|
updateCreatures w = appEndo f $ w
|
|
& creatures .~ IM.mapMaybe id m
|
|
& randGen .~ newg
|
|
where
|
|
((f,newg),m) = mapAccumR crUp (Endo id,_randGen w) (_creatures w)
|
|
crUp (f',g) cr = ((f' <> f'',g'), cr')
|
|
where
|
|
(f'',cr') = _crUpdate cr cr (w & randGen .~ g)
|
|
(_,g') = genWord8 g
|
|
{- |
|
|
Apply door mechanisms. -}
|
|
updateWalls :: World -> World
|
|
updateWalls w = IM.foldl' (flip $ fromMaybe id . (^? doorMech)) w (_walls w)
|
|
|
|
ppEvents :: World -> World
|
|
ppEvents w = IM.foldl' (flip $ \pp w' -> _ppEvent pp pp w') w $ _pressPlates w
|
|
|
|
updateSeenWalls :: World -> World
|
|
updateSeenWalls w = foldl' (flip markSeen) w wallsToUpdate
|
|
where
|
|
vPos = _cameraViewFrom w
|
|
wallsToUpdate = concatMap (\p -> visibleWalls vPos (vPos +.+p) $ wallsAlongLine vPos (vPos +.+ p) w)
|
|
$ nRays 20
|
|
markSeen i = set (walls . ix i . wlSeen) True
|
|
|
|
--setTestStringIO :: IO World -> IO World
|
|
--setTestStringIO = fmap (\ w -> set testString (show $ s w) w)
|
|
-- where
|
|
-- s w = (-.-) <$> (w ^? creatures . ix 0 . crPos) <*> (w ^? creatures . ix 0 . crOldPos)
|
|
|
|
checkEndGame :: World -> World
|
|
checkEndGame w
|
|
| _crHP (you w) < 1 = haltSound $ w {_menuLayers = [gameOverMenu]}
|
|
| otherwise = w
|
|
|
|
--updateClouds :: World -> World
|
|
--updateClouds w = w' & clouds %~ mapMaybe (updateCloud w)
|
|
updateClouds :: World -> World
|
|
updateClouds w = w' & clouds .~ catMaybes mclouds
|
|
where
|
|
(w',mclouds) = mapAccumR updateCloud w (_clouds w)
|
|
-- did i write a mapMaybeAccumR ?
|
|
|
|
updateCloud :: World -> Cloud -> (World,Maybe Cloud)
|
|
updateCloud w c
|
|
| _clTimer c < 1 = (w, Nothing)
|
|
| otherwise = (_clEffect c c w, Just $ c
|
|
& clPos .~ finalPos
|
|
& clVel .~ finalVel
|
|
& clTimer -~ 1
|
|
)
|
|
where
|
|
newVel@(V3 _ _ nvz) = (0.95 *.*.* springVels) +.+.+ V3 0 0 (0.001 * vertVel)
|
|
newVel2 = stripZ newVel
|
|
vertVel = _clAlt c - opz
|
|
springVels = foldl' (clClSpringVel c) (_clVel c) (cloudsNearPoint oldPos2 w)
|
|
oldPos@(V3 _ _ opz) = _clPos c
|
|
oldPos2 = stripZ oldPos
|
|
newPos@(V3 _ _ npz) = oldPos +.+.+ newVel
|
|
newPos2 = stripZ newPos
|
|
hitWl = collideCircWalls oldPos2 newPos2 5 $ wallsNearPoint newPos2 w
|
|
finalPos = addZ npz $ maybe newPos2 fst hitWl
|
|
finalVel = addZ nvz $ maybe newVel2 snd hitWl
|
|
|
|
--updateCloud :: World -> Cloud -> Maybe Cloud
|
|
--updateCloud w c
|
|
-- | _clTimer c < 1 = Nothing
|
|
-- | otherwise = Just $ c
|
|
-- & clPos .~ finalPos
|
|
-- & clVel .~ finalVel
|
|
-- & clTimer -~ 1
|
|
-- where
|
|
-- newVel = 0.95 *.* springVels
|
|
-- springVels = foldl' (clClSpringVel c) (_clVel c) (cloudsNearPoint oldPos w)
|
|
-- oldPos = _clPos c
|
|
-- newPos = oldPos +.+ newVel
|
|
-- hitWl = collideCircWalls' oldPos newPos 5 $ wallsNearPoint newPos w
|
|
-- finalPos = maybe newPos fst hitWl
|
|
-- finalVel = maybe newVel snd hitWl
|
|
|
|
clClSpringVel :: Cloud -> Point3 -> Cloud -> Point3
|
|
clClSpringVel a v b
|
|
| dist3 pa pb < radDist = v +.+.+ 0.1 *.*.* normalizeV3 (pa -.-.- pb)
|
|
| otherwise = v
|
|
where
|
|
pa = _clPos a
|
|
pb = _clPos b
|
|
radDist = (_clRad a + _clRad b) / 2
|
|
|
|
simpleCrSprings :: World -> World
|
|
simpleCrSprings w = IM.foldl' (flip crSpring) w $ _creatures w
|
|
|
|
crSpring :: Creature -> World -> World
|
|
crSpring c w = IM.foldl' (flip $ crCrSpring c) w cs
|
|
where
|
|
cs = creaturesNearPoint (_crPos c) w
|
|
|
|
crCrSpring :: Creature -> Creature -> World -> World
|
|
crCrSpring c1 c2 w
|
|
| id1 == id2 = w
|
|
| vec == V2 0 0 = w
|
|
| diff >= comRad = w
|
|
| otherwise = over (creatures . ix id1 . crPos) (+.+ overlap1)
|
|
$ over (creatures . ix id2 . crPos) (-.- overlap2) w
|
|
where
|
|
id1 = _crID c1
|
|
id2 = _crID c2
|
|
vec = _crPos c1 -.- _crPos c2
|
|
diff = magV vec
|
|
comRad = _crRad c1 + _crRad c2
|
|
overlap1 = ((comRad - diff) * _crMass c2 * 0.5 / massT) *.* errorNormalizeV 55 vec
|
|
overlap2 = ((comRad - diff) * _crMass c1 * 0.5 / massT) *.* errorNormalizeV 56 vec
|
|
massT = _crMass c1 + _crMass c2
|
|
|
|
ifConfigWallRotate :: World -> World
|
|
ifConfigWallRotate w
|
|
| _rotate_to_wall $ _config w = rotateToOverlappingWall w
|
|
| otherwise = w
|
|
|
|
rotateToOverlappingWall :: World -> World
|
|
rotateToOverlappingWall w = case theWall of
|
|
Nothing -> w
|
|
Just wl -> rotateUsing ( (argV . uncurry (-.-) $ _wlLine wl) - camrot)
|
|
where
|
|
camrot = _cameraRot w
|
|
rotateUsing a
|
|
| b - b' > 0.01 = w & cameraRot +~ 0.01
|
|
| b - b' < negate 0.01 = w & cameraRot -~ 0.01
|
|
| otherwise = w
|
|
where
|
|
b = a * (2 / pi)
|
|
b' = fromIntegral (round b :: Int)
|
|
cr = you w
|
|
p = _crPos cr
|
|
theWall = overlapCircWallsReturnWall p (_crRad cr + 5) (wallsNearPoint p w)
|
|
|
|
{-
|
|
Finds the IDs of visible walls from a point to another point. -}
|
|
visibleWalls :: Point2 -> Point2 -> IM.IntMap Wall -> [Int]
|
|
visibleWalls p1 p2 ws
|
|
= map fst
|
|
. takeUntil (_wlIsSeeThrough . snd)
|
|
. map snd
|
|
. sortOn (dist p1 . fromJust . fst)
|
|
. filter ((/=) Nothing . fst)
|
|
. map f
|
|
$ IM.toList ws
|
|
where
|
|
f (i,wl) = (uncurry intersectSegSeg (_wlLine wl) p1 p2, (i,wl))
|