234 lines
8.7 KiB
Haskell
234 lines
8.7 KiB
Haskell
module Dodge.Creature.State where
|
|
import Dodge.Data
|
|
import Dodge.Creature.State.Data
|
|
import Dodge.Creature.Stance.Data
|
|
--import Dodge.Creature.Test
|
|
import Dodge.Base
|
|
import Dodge.SoundLogic
|
|
import Dodge.RandomHelp
|
|
import Dodge.WorldEvent
|
|
--import Dodge.WallCreatureCollisions
|
|
import Dodge.Creature.Action
|
|
import Geometry
|
|
import Picture
|
|
import qualified IntMapHelp as IM
|
|
|
|
import Data.List
|
|
--import Data.Char
|
|
--import Data.Maybe
|
|
import Data.Function
|
|
import Control.Lens
|
|
--import Control.Applicative
|
|
import Control.Monad.State
|
|
--import Control.Monad
|
|
--import qualified SDL
|
|
--import qualified SDL.Mixer as Mix
|
|
import System.Random
|
|
--import qualified Data.Set as S
|
|
--import qualified Data.Map as M
|
|
|
|
type CRUpdate = World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
|
|
|
|
meleeCooldown :: CRUpdate -> CRUpdate
|
|
meleeCooldown u w (f,g) cr = u w (f,g) $ cr & crMeleeCooldown . _Just %~ (max 0 . (\x -> x - 1))
|
|
-- | The movement is updated before the ai in order to correctly set the oldpos.
|
|
-- the whole of this update cycle could do with a rethink, it is becoming
|
|
-- convoluted
|
|
stateUpdate :: CRUpdate -> CRUpdate
|
|
stateUpdate u w (f,g) cr =
|
|
let (cr', g'') = updateMovement g cr
|
|
in case u w (f,g'') cr' of
|
|
((f',g') , maybeCr) ->
|
|
( (invSideEff cr . movementSideEff cr . deathEff . f'
|
|
, g')
|
|
, fmap (stepReloading . stepItemUseCooldown . doDamage . crAutoReload) $ crOrCorpse =<< maybeCr
|
|
)
|
|
where
|
|
crOrCorpse cr'
|
|
| cr' ^. crHP > 0 = Just cr'
|
|
| otherwise = Nothing
|
|
deathEff
|
|
| cr ^.crHP > 0 = id
|
|
| otherwise = stopSoundFrom (CrWeaponSound (_crID cr))
|
|
. over decorations addCorpse
|
|
. dropByState cr
|
|
--crBeforeDeath = colCrWall w cr
|
|
addCorpse = IM.insertNewKey
|
|
$ uncurry translate (_crOldPos cr)
|
|
$ rotate (_crDir cr)
|
|
(_crCorpse cr)
|
|
-- | Drop items according to the creature state.
|
|
dropByState :: Creature -> World -> World
|
|
dropByState cr w = foldr (copyItemToFloor cr) w is
|
|
where
|
|
is :: [Int]
|
|
is = case cr ^. crState . crDropsOnDeath of
|
|
DropAll -> IM.keys $ _crInv cr
|
|
DropSpecific xs -> xs
|
|
DropAmount n -> take n $ evalState (shuffle $ IM.keys $ _crInv cr) (_randGen w)
|
|
|
|
setOldPos :: Creature -> Creature
|
|
setOldPos cr = set crOldPos (_crPos cr) cr
|
|
{- |
|
|
Given a creature and a velocity, applies friction to that creature and evaluates a
|
|
velocity to carry across frames.
|
|
-}
|
|
crFriction :: Creature -> Point2 -> Point2
|
|
crFriction _ _ = (0,0)
|
|
|
|
doDamage :: Creature -> Creature
|
|
doDamage cr = set (crState . crDamage) []
|
|
$ over (crState . crPastDamage) (forceList . take 20 . (dams :) ) damagedCr
|
|
where
|
|
dams = _crDamage $ _crState cr
|
|
damagedCr = snd $ _crApplyDamage cr dams cr
|
|
|
|
movementSideEff :: Creature -> World -> World
|
|
movementSideEff cr w
|
|
| hasJetPack
|
|
= case cr ^? crStance . carriage of
|
|
Just (Boosting v)
|
|
-> makeFlameletTimed
|
|
(oldPos +.+ (_crRad cr + 3) *.* unitVectorAtAngle (_crDir cr + pi))
|
|
(momentum +.+ 1 *.* rotateV randDir (vInverse v))
|
|
Nothing
|
|
1
|
|
20
|
|
$ set randGen g
|
|
w
|
|
_ -> w
|
|
| otherwise = case cr ^? crStance . carriage of
|
|
Just (Walking x y) -> takeStep x y w
|
|
_ -> w
|
|
where
|
|
hasJetPack = any (\it -> it ^? itIdentity == Just JetPack) $ _crInv cr
|
|
oldPos = _crOldPos cr
|
|
momentum' = 0.97 *.* (_crPos cr -.- _crOldPos cr)
|
|
momentum'' | magV momentum' > 3 = 3 *.* normalizeV momentum'
|
|
| otherwise = momentum'
|
|
momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng
|
|
(randDir,g) = randomR (-0.5,0.5) $ _randGen w
|
|
(randAng,_) = randomR (0,2*pi) $ _randGen w
|
|
|
|
crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5
|
|
takeStep x y | crHasMoved && x < 20 && x + y >= 20 = soundMultiFrom footor 22 3 0
|
|
| crHasMoved && x < 80 && x + y >= 80 = soundMultiFrom footor 23 3 0
|
|
| otherwise = id
|
|
footor = [FootstepSound 0,FootstepSound 1]
|
|
|
|
invSideEff :: Creature -> World -> World
|
|
invSideEff cr w = weaponReloadSounds cr $ IM.foldrWithKey f w (_crInv cr)
|
|
where
|
|
f i it w' = case it ^? itEffect . itInvEffect of
|
|
Nothing -> w'
|
|
Just g -> g cr i w'
|
|
|
|
weaponReloadSounds :: Creature -> World -> World
|
|
weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of
|
|
Just Weapon{_wpReloadState = 0} -> w
|
|
Just Weapon{_wpReloadState = 1} -> stopSoundFrom (CrReloadSound cid) w
|
|
Just Weapon{} -> soundFrom (CrReloadSound cid) reloadSound 1 0 w
|
|
_ -> w
|
|
where
|
|
cid = _crID cr
|
|
|
|
updateMovement :: StdGen -> Creature -> (Creature, StdGen)
|
|
updateMovement g cr
|
|
| isFrictionless cr = (over crPos (+.+ momentum) $ setOldPos cr, g')
|
|
| otherwise = case cr ^? crStance . carriage of
|
|
Just (Walking x y)
|
|
-> (set (crStance . carriage) (Walking ((x+y)`mod`120) 0) $ setOldPos cr
|
|
, g)
|
|
_ -> (set (crStance . carriage) (Walking 0 0) $ setOldPos cr
|
|
, g)
|
|
where
|
|
momentum' = 0.98 *.* (_crPos cr -.- _crOldPos cr)
|
|
momentum'' | magV momentum' > 1 = 1 *.* normalizeV momentum'
|
|
| otherwise = momentum'
|
|
momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng
|
|
(randAng,g') = randomR (0,2*pi) g
|
|
|
|
isFrictionless :: Creature -> Bool
|
|
isFrictionless cr = case cr ^? crStance . carriage of
|
|
Just (Boosting _) -> True
|
|
Just Floating -> True
|
|
_ -> False
|
|
|
|
stepItemUseCooldown :: Creature -> Creature
|
|
stepItemUseCooldown cr = over (crInv . ix iSel . itUseTime) decreaseToZero cr
|
|
where
|
|
iSel = _crInvSel cr
|
|
|
|
stepReloading :: Creature -> Creature
|
|
stepReloading cr
|
|
-- | crIsAiming' cr = cr
|
|
-- | otherwise
|
|
= over (crInv . ix iSel . wpReloadState) decreaseToZero cr
|
|
where
|
|
iSel = _crInvSel cr
|
|
|
|
decreaseToZero :: Int -> Int
|
|
decreaseToZero x = max 0 (x - 1)
|
|
|
|
onDeath :: (Creature -> World -> World) -> CRUpdate -> CRUpdate
|
|
onDeath h u w (f,g) cr
|
|
| _crHP cr > 0 = u w (f,g) cr
|
|
| otherwise = ( ( h cr . f, g ) , Nothing )
|
|
|
|
updateBarrel
|
|
:: World
|
|
-> (World -> World,StdGen)
|
|
-> Creature
|
|
-> ((World -> World , StdGen), Maybe Creature)
|
|
updateBarrel _ (f,g) cr
|
|
| _crHP cr > 0 = ((f, g), newCr)
|
|
| otherwise = ((f, g), Nothing)
|
|
where
|
|
newCr = Just $ doDamage cr
|
|
|
|
-- it is easy to leave off the "f" here
|
|
-- should find some better way of doing all this that is less prone to error
|
|
updateExpBarrel ::
|
|
World -> (World -> World,StdGen) -> Creature -> ((World -> World , StdGen), Maybe Creature)
|
|
updateExpBarrel _ (f,g) cr
|
|
| _crHP cr > 0 = ((f . foldr (.) id pierceSparks . hiss, g'), newCr)
|
|
| otherwise = ((f . makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing)
|
|
where
|
|
damages = _crDamage $ _crState cr
|
|
pierceSparks :: [World -> World]
|
|
pierceSparks
|
|
= zipWith4 (\p a -> createBarrelSpark (_crPos cr +.+ p) (a + argV p) (Just $ _crID cr))
|
|
poss as times colids
|
|
as = randomRs (-0.7,0.7) g
|
|
colids = randomRs (0,11) g
|
|
times = randomRs (2,5) g
|
|
(g',_) = split g
|
|
poss = _piercedPoints $ _crSpState $ _crState cr
|
|
--newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages
|
|
newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr
|
|
--perforate :: DamageType -> Creature -> Creature
|
|
--perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints)
|
|
-- ((:) $ int -.- _crPos cr) cr
|
|
--perforate _ cr = cr
|
|
applyFuseDamage cr' = cr' & crHP %~
|
|
subtract (length . _piercedPoints . _crSpState $ _crState cr')
|
|
hiss | null poss = id
|
|
| otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1
|
|
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1)
|
|
|
|
damToExpBarrel :: [DamageType] -> Creature -> Creature
|
|
damToExpBarrel ds cr = foldr damToExpBarrel' (foldr damToExpBarrel' cr pierceDam) otherDam
|
|
where
|
|
(pierceDam,otherDam) = partition isPierce ds
|
|
isPierce Piercing{} = True
|
|
isPierce _ = False
|
|
|
|
damToExpBarrel' :: DamageType -> Creature -> Creature
|
|
damToExpBarrel' (Piercing amount _ int _) cr
|
|
= over (crState . crSpState . piercedPoints) ((:) $ int -.- _crPos cr)
|
|
$ over crHP (\hp -> hp - div amount 200) cr
|
|
damToExpBarrel' PoisonDam {} cr = cr
|
|
damToExpBarrel' SparkDam {} cr = cr
|
|
damToExpBarrel' PushDam{_dmPushBack = v} cr = cr Control.Lens.& crPos %~ (+.+) (1 / _crMass cr *.* v)
|
|
damToExpBarrel' dt cr = cr Control.Lens.& crHP -~ _dmAmount dt
|