edge-0.8.2: Step.hs
{-# LANGUAGE ExistentialQuantification, Rank2Types #-}
module Step (stepUniverse) where
import Universe
import SpaceJunk
import Moving
import Data.WrapAround
import Updating
import GHC.Float
import Lance
import Data.List (find)
import Combat
import Data.Maybe ( fromMaybe
, isNothing
, fromJust
, isJust
)
import AfterEffect
import qualified AfterEffect.SimpleExplosion as SimpleExplosion
import Unit
import Animation
import Control.Monad
import System.IO
import System.Exit
import Resources
import Sound.ALUT
import Common
delayOnDeath = 4.0
stepUniverse :: Float -> Universe -> IO Universe
stepUniverse t u =
let t' = float2Double t in
do (_, u') <- (handleNewLevel
>=> handleLives
>=> handlePureUpdates
>=> handleSound) (t', u)
return u'
handleLives :: (Time, Universe) -> IO (Time, Universe)
handleLives (t, u) =
do let a = arena u
let wmap = Universe.wrapMap a
let rt = resourceTracker u
if isJust (lance (arena u))
then return (t, u)
else if delayRemaining u > 0
then return ( t
, u { delayRemaining = delayRemaining u - t }
)
else if lives u > 0
then return
( t
, u { arena =
a { lance = Just (Lance.new rt wmap
(wrappoint wmap (0, 0)))
}
, delayRemaining = delayOnDeath
}
)
else return
( t
, u { arena =
(head (levels u))
{ lance = Just (Lance.new rt wmap
(wrappoint wmap (0, 0)))
}
, level = 0
, lives = 3
, delayRemaining = delayOnDeath
}
)
handleNewLevel :: (Time, Universe) -> IO (Time, Universe)
handleNewLevel (t, u)
| not (null (simpleUnits (arena u)) && null (smartUnits (arena u)))
&& not (skipLevel u)
= return (t, u)
| level u + 1 >= length (Universe.levels u) =
do hPutStrLn stderr "Out of levels! Game over."
exitSuccess
| otherwise =
do silenceMost (arena u)
let a = Universe.levels u !! (level u + 1)
let l = lance (arena u)
let wmap = Universe.wrapMap a
let u'
= u { arena =
a { lance =
fmap
(\ l ->
l { Lance.center = wrappoint wmap
(0.0, 0.0)
, angle = 0.0
, Lance.velocity = (0.0, 0.0)})
l
}
, level = level u + 1
, skipLevel = False
, levelMessageTimer = Just 0.0
}
return (t, u')
{-
Here we'll terminateAudio everything in the arena that can receive it, not
including the Lance, which is carried across level changes.
-}
silenceMost a = return ()
-- newLevel u = -- let mProcessAudio = mapM processAudio in
-- let a = arena in
-- if level u >= length (levels u)
-- then
handlePureUpdates :: (Time, Universe) -> IO (Time, Universe)
handlePureUpdates (t, u) = return $ ( handleExpiration
. handlePostUpdates
. handleCollisions
. handleUnitLaunches
. handleLanceLaunches
. handlePreUpdates
. handleVisionUpdates
. fixFocus
. handleUniverseCounters
) (t, u)
handleUniverseCounters :: (Time, Universe) -> (Time, Universe)
handleUniverseCounters (t, u) =
let u' = u { levelMessageTimer = case levelMessageTimer u of
Nothing -> Nothing
Just mt -> if mt > 1.5
then Nothing
else Just (mt + t)
, panelActivationTimer = panelActivationTimer u + t
} in
(t, u')
handleSound :: (Time, Universe) -> IO (Time, Universe)
handleSound (t, u) =
let arena' = arena u in
do let listenerCoords = case lance arena' of
Nothing -> lastFocus arena'
Just l -> Moving.center l
lance' <- case lance arena' of
Nothing -> return Nothing
Just l -> do l' <- processAudio l listenerCoords
return (Just l')
simpleUnits' <- mapM (\x -> processAudio x listenerCoords) (simpleUnits arena')
smartUnits' <- mapM (\x -> processAudio x listenerCoords) (smartUnits arena')
afterFX' <- mapM (\x -> processAudio x listenerCoords) (afterFX arena')
let u' = u { arena = arena' { lance = lance'
, simpleUnits = simpleUnits'
, smartUnits = smartUnits'
, afterFX = afterFX'
}
}
-- case lance' of
-- Nothing -> return ()
-- Just l -> do let (x, y) = toCoords
-- (Universe.wrapMap arena')
-- (Moving.center l)
-- listenerPosition $= (Vertex3
-- (double2Float x)
-- (double2Float y)
-- 0)
return (t, u')
handleExpiration :: (Time, Universe) -> (Time, Universe)
handleExpiration (t, u) =
let arena' = arena u in
let (lance', aFX_lance) = case lance arena' of
Nothing -> (Nothing, [])
Just l -> case expired' l of
Nothing -> (Just l, [])
Just a -> (Nothing, a) in
let lives' = if isJust (lance arena') && isNothing lance'
then lives u - 1
else lives u in
let (lanceProjectiles', aFX_lanceProjectiles) =
expirationFold (lanceProjectiles arena') in
let (unitProjectiles', aFX_unitProjectiles) =
expirationFold (unitProjectiles arena') in
let (afterFX', aFX_afterFX) =
expirationFold (afterFX arena') in
let (simpleUnits', aFX_simpleUnits) =
expirationFold (simpleUnits arena') in
let (smartUnits', aFX_smartUnits) =
expirationFold (smartUnits arena') in
let u' = u { arena = arena' { lance = lance'
, lanceProjectiles = lanceProjectiles'
, unitProjectiles = unitProjectiles'
, simpleUnits = simpleUnits'
, smartUnits = smartUnits'
, afterFX = afterFX'
++ aFX_afterFX
++ aFX_lance
++ aFX_lanceProjectiles
++ aFX_unitProjectiles
++ aFX_simpleUnits
++ aFX_smartUnits
}
, lives = lives'
} in
(t, u')
where expirationFold xs = foldr foldF ([], []) xs
foldF x (nxs, nAFX) = case expired' x of
Nothing -> (nxs ++ [x], nAFX)
Just a -> (nxs, nAFX ++ a)
handleLanceLaunches :: (Time, Universe) -> (Time, Universe)
handleLanceLaunches (t, u) =
let arena' = arena u in
case lance arena' of
Nothing -> (t, u)
Just llance -> let (nProj, nLance) = deployProjectiles llance in
let u' = u { arena = arena' { lanceProjectiles =
lanceProjectiles arena'
++ nProj
, lance = Just nLance
}
} in
(t, u')
handleUnitLaunches :: (Time, Universe) -> (Time, Universe)
handleUnitLaunches (t, u) =
let arena' = arena u in
let (simpleUnits', unitProjectiles') =
foldr foldDepl ([], []) (simpleUnits arena') in
let (smartUnits', unitProjectiles'') =
foldr foldDepl ([], []) (smartUnits arena') in
let u' = u { arena = arena' { unitProjectiles = unitProjectiles arena'
++ unitProjectiles'
++ unitProjectiles''
, simpleUnits = simpleUnits'
, smartUnits = smartUnits'
}
} in
(t, u')
where foldDepl s (units, projectiles) =
let (nProjectiles, updatedUnit) = deployProjectiles s in
(units ++ [updatedUnit], projectiles ++ nProjectiles)
fixFocus (t, u) =
let arena' = arena u in
let oldFocus = lastFocus arena' in
let mFocus = do llance <- lance arena'
Just (Moving.center llance) in
let newFocus = fromMaybe oldFocus mFocus in
let u' = u { arena = arena' { lastFocus = newFocus } } in
(t, u')
handleUpdatesCore :: (forall a. (InternallyUpdating a
=> (a -> Time -> a)))
-> (Time, Universe)
-> (Time, Universe)
handleUpdatesCore f (t, u) =
let arena' = arena u in
let lance' = do llance <- lance arena'
Just (f llance t) in
let asteroids' = [ f a t | a <- asteroids arena' ] in
let lanceProjectiles' = [ Projectile (f a t)
| Projectile a <- lanceProjectiles arena' ] in
let unitProjectiles' = [ Projectile (f a t)
| Projectile a <- unitProjectiles arena' ] in
let afterFX' = [ case effect of
AfterEffect a -> AfterEffect (f a t)
| effect <- afterFX arena' ] in
let simpleUnits' = [ f s t | s <- simpleUnits arena' ] in
let smartUnits' = [ f s t | s <- smartUnits arena' ] in
let u' = u { arena = arena' { lance = lance'
, asteroids = asteroids'
, lanceProjectiles = lanceProjectiles'
, afterFX = afterFX'
, simpleUnits = simpleUnits'
, smartUnits = smartUnits'
, unitProjectiles = unitProjectiles'
}
} in
(t, u')
handlePreUpdates :: (Time, Universe) -> (Time, Universe)
handlePreUpdates (t, u) = handleUpdatesCore Updating.preUpdate (t, u)
handlePostUpdates :: (Time, Universe) -> (Time, Universe)
handlePostUpdates (t, u) = handleUpdatesCore Updating.postUpdate (t, u)
handleVisionUpdates :: (Time, Universe) -> (Time, Universe)
handleVisionUpdates (t, u) =
let a = arena u in
let u' = u { arena =
a { smartUnits = [ updateVision s a | s <- smartUnits a ] }
} in
(t, u')
handleCollisionsLanceAsteroids (t, u) =
let lance' = case lance a of
Nothing -> Nothing
Just l -> let (x, _) = handleCollisionDamage
wmap t l (asteroids a) in
Just x in
let u' = u { arena = a { lance = lance' } } in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceUnitProjectiles (t, u) =
let (lance', unitProjectiles') =
case lance a of
Nothing -> (Nothing, unitProjectiles a)
Just l -> let (x, ys) = handleCollisionDamage
wmap t l (unitProjectiles a) in
(Just x, ys) in
let u' = u { arena = a { lance = lance'
, unitProjectiles = unitProjectiles'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceProjectilesAsteroids (t, u) =
let (lanceProjectiles', _) = handleCollisionDamage'
wmap t
(lanceProjectiles a)
(asteroids a) in
let u' = u { arena = a { lanceProjectiles = lanceProjectiles'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceProjectilesSimpleUnits (t, u) =
let (lanceProjectiles', simpleUnits') = handleCollisionDamage'
wmap t
(lanceProjectiles a)
(simpleUnits a) in
let u' = u { arena = a { lanceProjectiles = lanceProjectiles'
, simpleUnits = simpleUnits'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsUnitProjectilesAsteroids (t, u) =
let (unitProjectiles', _) = handleCollisionDamage'
wmap t
(unitProjectiles a)
(asteroids a) in
let u' = u { arena = a { unitProjectiles = unitProjectiles'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceSimpleUnits (t, u) =
let (lance', simpleUnits') =
case (lance a) of
Nothing -> (Nothing, (simpleUnits a))
Just l -> let (x, ys) = handleCollisionDamage
wmap t l (simpleUnits a) in
(Just x, ys) in
let u' = u { arena = a { lance = lance'
, simpleUnits = simpleUnits'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceProjectilesSmartUnits (t, u) =
let (lanceProjectiles', smartUnits') = handleCollisionDamage'
wmap t
(lanceProjectiles a)
(smartUnits a) in
let u' = u { arena = a { lanceProjectiles = lanceProjectiles'
, smartUnits = smartUnits'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceSmartUnits (t, u) =
let (lance', smartUnits') =
case (lance a) of
Nothing -> (Nothing, (smartUnits a))
Just l -> let (x, ys) = handleCollisionDamage
wmap t l (smartUnits a) in
(Just x, ys) in
let u' = u { arena = a { lance = lance'
, smartUnits = smartUnits'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
handleCollisionsLanceItems (t, u) =
let (lance', items') =
case (lance a) of
Nothing -> (Nothing, (items a))
Just l -> let (x, ys) = collisionHandler l (items a) [] in
(Just x, ys) in
let u' = u { arena = a { lance = lance'
, items = items'
}
} in
(t, u')
where
a = arena u
wmap = Universe.wrapMap a
collisionHandler x [] nys = (x, nys)
collisionHandler x (y:ys) nys =
if not (collisionWindow wmap (max
(maxExpectedVelocity * t)
(collisionRadius x + collisionRadius y)) x y)
then collisionHandler x ys (nys ++ [y])
else
case collision wmap t x y of
Nothing -> collisionHandler x ys (nys ++ [y])
Just _ -> let nx = processItem x y in
collisionHandler nx ys nys
handleCollisions :: (Time, Universe) -> (Time, Universe)
handleCollisions (t, u) =
( handleCollisionsLanceItems
. handleCollisionsLanceSmartUnits
. handleCollisionsLanceProjectilesSmartUnits
. handleCollisionsLanceSimpleUnits
. handleCollisionsUnitProjectilesAsteroids
. handleCollisionsLanceProjectilesSimpleUnits
. handleCollisionsLanceProjectilesAsteroids
. handleCollisionsLanceUnitProjectiles
. handleCollisionsLanceAsteroids
) (t, u)