81f88c32e9
Both now use Ints for presses. Still no release timer for keyboard events.
401 lines
14 KiB
Haskell
401 lines
14 KiB
Haskell
module Dodge.Creature.State (
|
|
crUpdate,
|
|
doDamage,
|
|
) where
|
|
|
|
import Control.Applicative
|
|
import Control.Monad
|
|
import qualified Data.Map.Strict as M
|
|
import Data.Maybe
|
|
import Data.Monoid
|
|
import Dodge.Base
|
|
import Dodge.BaseTriggerType
|
|
import Dodge.Corpse.Make
|
|
import Dodge.Creature.Action
|
|
import Dodge.Creature.Damage
|
|
import Dodge.Creature.Impulse.Movement
|
|
import Dodge.Creature.State.WalkCycle
|
|
import Dodge.Creature.Test
|
|
import Dodge.Damage
|
|
import Dodge.Data.ComposedItem
|
|
import Dodge.Data.Damage.Type
|
|
import Dodge.Data.DoubleTree
|
|
--import Dodge.Data.DoubleTree
|
|
import Dodge.Data.World
|
|
import Dodge.DoubleTree
|
|
import Dodge.Euse
|
|
import Dodge.HeldUse
|
|
import Dodge.Inventory.SelectionList
|
|
import Dodge.Item.BackgroundEffect
|
|
import Dodge.Item.Grammar
|
|
import Dodge.Item.HeldOffset
|
|
import Dodge.Item.Location
|
|
import Dodge.Item.MaxAmmo
|
|
import Dodge.Prop.Gib
|
|
import Dodge.SoundLogic
|
|
import Dodge.Targeting.Draw
|
|
import Dodge.Zoning.Creature
|
|
import FoldableHelp
|
|
import Geometry
|
|
import qualified IntMapHelp as IM
|
|
import LensHelp
|
|
import Picture
|
|
import qualified Quaternion as Q
|
|
import RandomHelp
|
|
import qualified SDL
|
|
import Shape
|
|
import ShapePicture
|
|
|
|
-- this can almost certainly be made more efficient
|
|
foldCr :: [Creature -> World -> World] -> Creature -> World -> World
|
|
foldCr xs cr w = foldl' f w xs
|
|
where
|
|
f w' g = case w' ^? cWorld . lWorld . creatures . ix (_crID cr) of
|
|
Just cr' -> g cr' w'
|
|
Nothing -> w'
|
|
|
|
{- | this seems to work, but I am not sure about the ordering:
|
|
previously, the movement was updated before the ai in order to correctly set the oldpos.
|
|
This should be made more sensible: should the movement side effects apply to
|
|
the creature before or after it has moved?
|
|
at what point invSideEffects is applied wrt to when the creature moves
|
|
may affect whether the shield moves correctly
|
|
-}
|
|
crUpdate :: (Creature -> World -> World) -> Creature -> World -> World
|
|
crUpdate f =
|
|
foldCr
|
|
[ doDamage -- these two
|
|
, checkDeath -- must be in this order 24/7/22
|
|
, updateWalkCycle
|
|
, f
|
|
, invItemEffs
|
|
]
|
|
|
|
-- I have changed the ordering of item/equipment effects, which may have
|
|
-- unforseen consequences, be aware
|
|
|
|
checkDeath :: Creature -> World -> World
|
|
checkDeath cr w
|
|
| _crHP cr > 0 =
|
|
w
|
|
& cWorld . lWorld . creatures . ix (_crID cr) . crDamage .~ []
|
|
| _crHP cr <= -200 =
|
|
w
|
|
& dropAll cr -- the order of
|
|
& removecr -- these is important
|
|
& stopSoundFrom (CrWeaponSound (_crID cr) 0)
|
|
& addCrGibs cr
|
|
| _crHP cr > -200 && _crDeathTimer cr < 5 =
|
|
w
|
|
& cWorld . lWorld . creatures . ix (_crID cr) . crDamage .~ []
|
|
& cWorld . lWorld . creatures . ix (_crID cr) . crDeathTimer +~ 1
|
|
| otherwise =
|
|
w
|
|
& dropAll cr -- the order of
|
|
& removecr -- these is important
|
|
& stopSoundFrom (CrWeaponSound (_crID cr) 0)
|
|
& corpseOrGib cr
|
|
where
|
|
removecr
|
|
| _crID cr == 0 = id
|
|
-- cWorld . lWorld . creatures . ix (_crID cr) . crHP .~ 0
|
|
-- hack to get around player creature being killed but left with more than 0 hp
|
|
| otherwise = cWorld . lWorld . creatures . at (_crID cr) .~ Nothing
|
|
|
|
-- could look at the amount of damage here (given by maxDamage) too
|
|
corpseOrGib :: Creature -> World -> World
|
|
corpseOrGib cr = case cr ^? crDamage . to maxDamageType . _Just . _1 of
|
|
Just CookingDamage -> addcorpse (thecorpse & cpSPic %~ scorchSPic)
|
|
Just PoisonDamage -> addcorpse (thecorpse & cpSPic %~ poisonSPic)
|
|
Just PhysicalDamage | _crPain cr > 200 -> addCrGibs cr
|
|
_ -> addcorpse thecorpse
|
|
where
|
|
addcorpse ctype = plNew (cWorld . lWorld . corpses) cpID ctype
|
|
thecorpse = makeCorpse cr
|
|
|
|
scorchSPic :: SPic -> SPic
|
|
scorchSPic = _1 %~ overColSH (mixColors 0.9 0.1 black . normalizeColor)
|
|
|
|
poisonSPic :: SPic -> SPic
|
|
poisonSPic = _1 %~ overColSH (mixColors 0.5 0.5 green . normalizeColor)
|
|
|
|
{- | Drop items according to the creature state.
|
|
TODO make sure this doesn't mess up any ItemPosition
|
|
-}
|
|
dropAll :: Creature -> World -> World
|
|
dropAll cr w = foldl' (flip (dropItem cr)) w $ IM.keys $ _crInv cr
|
|
|
|
doDamage :: Creature -> World -> World
|
|
doDamage cr = applyPastDamages cr . applyCreatureDamage (cr ^. crDamage) cr
|
|
|
|
-- TODO generalise shake to arbitrary damage amounts
|
|
applyPastDamages :: Creature -> World -> World
|
|
applyPastDamages cr w
|
|
| _crPain cr > 200 = dojitter 3 100
|
|
| _crPain cr > 20 = dojitter 2 10
|
|
| _crPain cr > 0 = dojitter 1 1
|
|
| otherwise = w
|
|
where
|
|
dojitter x y =
|
|
let (p, g) = runState (randInCirc x) (_randGen w)
|
|
in w & cWorld . lWorld . creatures . ix (_crID cr) %~ crMvBy p
|
|
& cWorld . lWorld . creatures . ix (_crID cr) . crPain -~ y
|
|
& randGen .~ g
|
|
|
|
-- a loop going over all root inventory items
|
|
invItemEffs :: Creature -> World -> World
|
|
invItemEffs cr =
|
|
appEndo $
|
|
foldMap
|
|
(reduceLocDT (Endo . invItemLocUpdate cr) . LocDT TopDT)
|
|
(invDT' (_crInv cr))
|
|
|
|
invItemLocUpdate :: Creature -> LocationDT OItem -> World -> World
|
|
invItemLocUpdate cr loc w = doAnyEquipmentEffect loc cr $ case itm ^. itType of
|
|
ATTACH BULLETSYNTH -> trySynthBullet loc w
|
|
ATTACH CAPACITOR -> tryDrawToCapacitor loc w
|
|
EQUIP WRIST_ECG | haspulse -> tryUseParent loc w
|
|
COPIER _ -> copierItemUpdate itm cr w
|
|
HELD FLATSHIELD -> rootNotrootEff createShieldWall removeShieldWall itm cr w
|
|
HELD MINIGUNX{} -> coolMinigun itm w
|
|
HELD MACHINEPISTOL{} -> coolMachinePistol cr itm w
|
|
HELD LASER | loc ^. locDT . dtValue . _2 == WeaponTargetingSF
|
|
, itm ^? itLocation . ilIsAttached == Just True -> shineTargetLaser cr loc w
|
|
HELD TORCH
|
|
| itm ^? itLocation . ilIsAttached == Just True -> shineTorch cr loc w
|
|
TARGETING tt
|
|
| itm ^? itLocation . ilIsAttached == Just True -> updateItemTargeting tt cr itm w
|
|
ARHUD
|
|
| itm ^? itLocation . ilIsAttached == Just True -> drawARHUD loc w
|
|
_ -> w
|
|
where
|
|
haspulse =
|
|
w ^? cWorld . lWorld . creatures . ix 0 . crType . avatarPulse . pulseProgress
|
|
== Just 0
|
|
itm = loc ^. locDT . dtValue . _1
|
|
|
|
coolMinigun :: Item -> World -> World
|
|
coolMinigun itm
|
|
| itm ^? itParams . isWarming == Just True = d . isWarming .~ False
|
|
| otherwise = d . wTime %~ (max 0 . subtract 1)
|
|
where
|
|
d = pointerToItem itm . itParams
|
|
|
|
coolMachinePistol :: Creature -> Item -> World -> World
|
|
coolMachinePistol cr itm w
|
|
| fromMaybe 0 (itm ^? itParams . wTime) >= _coolEnd (baseItemTriggerType itm) =
|
|
w & d . wTime .~ 0
|
|
& d . coolSound .~ Nothing
|
|
| fromMaybe 0 (itm ^? itParams . wTime) == _coolStart (baseItemTriggerType itm) =
|
|
w
|
|
& randGen .~ g
|
|
& d . wTime +~ 1
|
|
& d . coolSound ?~ sid
|
|
& dosound sid
|
|
| fromMaybe 0 (itm ^? itParams . wTime) > _coolStart (baseItemTriggerType itm) =
|
|
w
|
|
& d . wTime +~ 1
|
|
& dosound (fromMaybe undefined (itm ^? itParams . coolSound . _Just))
|
|
| itm ^? itParams . isWarming == Just True = w & d . isWarming .~ False
|
|
| fromMaybe 0 (itm ^? itParams . wTime) >= _warmStart (baseItemTriggerType itm) =
|
|
w & d . wTime .~ _coolStart (baseItemTriggerType itm)
|
|
| otherwise = w & d . wTime .~ 0
|
|
where
|
|
dosound s =
|
|
soundContinue
|
|
(CrWeaponSound (_crID cr) (fromIntegral $ _itID itm))
|
|
(_crPos cr)
|
|
s
|
|
(Just 1)
|
|
d = pointerToItem itm . itParams
|
|
(sid, g) = runState (takeOne [whirdownSmall1S, whirdownSmall2S]) (w ^. randGen)
|
|
|
|
copierItemUpdate :: Item -> Creature -> World -> World
|
|
copierItemUpdate itm cr w = fromMaybe w $ do
|
|
x <- itm ^? itScroll . itsInt
|
|
invid <- itm ^? itLocation . ilInvID
|
|
ip <- itm ^? itType . ibtPathing
|
|
i <- getInventoryPath x ip invid cr
|
|
itm' <- cr ^? crInv . ix i
|
|
v <- getItemValue itm' w cr
|
|
return $ w & pointerToItem itm . itUse . uValue .~ v
|
|
|
|
doAnyEquipmentEffect :: LocationDT OItem -> Creature -> World -> World
|
|
doAnyEquipmentEffect loc cr = maybe id (equipBackgroundEffect loc cr)
|
|
$ loc ^? locDT . dtValue . _1 . itLocation . ilEquipSite . _Just
|
|
|
|
tryUseParent :: LocationDT OItem -> World -> World
|
|
tryUseParent loc w = fromMaybe w $ do
|
|
t <- locUp' loc
|
|
cr <- w ^? cWorld . lWorld . creatures . ix 0
|
|
return $ gadgetEffect 0 t cr w
|
|
|
|
tryDrawToCapacitor :: LocationDT OItem -> World -> World
|
|
tryDrawToCapacitor loc w = fromMaybe w $ do
|
|
itm <- loc ^? locDT . dtValue . _1
|
|
i <- itm ^? itLocation . ilInvID
|
|
x <- loc ^? locDT . dtValue . _1 . itConsumables . _Just
|
|
guard $ x < 200
|
|
bat <- loc ^? locDT . dtLeft . ix 0 . dtValue . _1
|
|
j <- bat ^? itLocation . ilInvID
|
|
y <- bat ^? itConsumables . _Just
|
|
let z = min y 10
|
|
return $ w
|
|
& invpoint . ix i . itConsumables . _Just +~ z
|
|
& invpoint . ix j . itConsumables . _Just -~ z
|
|
where
|
|
invpoint = cWorld . lWorld . creatures . ix 0 . crInv
|
|
|
|
trySynthBullet :: LocationDT OItem -> World -> World
|
|
trySynthBullet loc w = fromMaybe w $ do
|
|
i <- itm ^? itLocation . ilInvID
|
|
x <- itm ^? itUse . uaParams . apInt
|
|
if x < 100
|
|
then do
|
|
bat <- loc ^? locDT . dtLeft . ix 0 . dtValue . _1
|
|
j <- bat ^? itLocation . ilInvID
|
|
y <- bat ^. itConsumables
|
|
guard $ y > 0
|
|
return $ w
|
|
& invpoint . ix i . itUse . uaParams . apInt +~ 1
|
|
& invpoint . ix j . itConsumables . _Just -~ 1
|
|
else do
|
|
mag <- loc ^? locDtContext . cdtParent . _1
|
|
j <- mag ^? itLocation . ilInvID
|
|
y <- mag ^. itConsumables
|
|
ymax <- maxAmmo mag
|
|
guard $ y < ymax
|
|
return $ w
|
|
& invpoint . ix i . itUse . uaParams . apInt .~ 0
|
|
& invpoint . ix j . itConsumables . _Just +~ 1
|
|
where
|
|
itm = loc ^. locDT . dtValue . _1
|
|
invpoint = cWorld . lWorld . creatures . ix 0 . crInv
|
|
|
|
drawARHUD :: LocationDT OItem -> World -> World
|
|
drawARHUD (LocDT con _) w = fromMaybe w $ do
|
|
itm <- con ^? cdtParent . _1
|
|
return $
|
|
w & cWorld . lWorld . flares
|
|
<>~ fold
|
|
(drawTargetingAR itm w <|> drawMapperAR itm w)
|
|
|
|
shineTargetLaser :: Creature -> LocationDT OItem -> World -> World
|
|
shineTargetLaser cr loc w = fromMaybe (w & pointittarg . itTgPos .~ Nothing) $ do
|
|
guard (crIsAiming cr)
|
|
mag <- find (isammolink . (^. dtValue . _2)) (itmtree ^. dtLeft)
|
|
i <- mag ^. dtValue . _1 . itConsumables
|
|
guard $ i >= x
|
|
maginvid <- mag ^? dtValue . _1 . itLocation . ilInvID
|
|
return $
|
|
w
|
|
& worldEventFlags . at InventoryChange ?~ ()
|
|
& cWorld . lWorld . creatures . ix (_crID cr)
|
|
. crInv
|
|
. ix maginvid
|
|
. itConsumables
|
|
. _Just
|
|
-~ x
|
|
& cWorld . lWorld . lasers
|
|
.:~ Laser
|
|
{ _lpPhaseV = 1
|
|
, _lpDir = _crDir cr + argV (Q.qToV2 q)
|
|
, _lpPos = pos
|
|
, _lpColor = col
|
|
, _lpType = TargetingLaser (_itID itm)
|
|
}
|
|
where
|
|
o = locOrient loc cr
|
|
itmtree = loc ^. locDT
|
|
(p, q) = o `Q.comp` (V3 5 0 0, Q.qID)
|
|
x = 1
|
|
isammolink AmmoMagSF{} = True
|
|
isammolink _ = False
|
|
pos = _crPos cr + xyV3 (rotate3 cdir p)
|
|
cdir = _crDir cr
|
|
itm = itmtree ^. dtValue . _1
|
|
pointittarg = cWorld . lWorld . creatures . ix cid . crInv . ix invid . itTargeting
|
|
cid = _crID cr
|
|
invid = _ilInvID $ _itLocation itm
|
|
col = blue -- mixColors reloadFrac (1-reloadFrac) blue red
|
|
|
|
shineTorch :: Creature -> LocationDT OItem -> World -> World
|
|
shineTorch cr loc = fromMaybe id $ do
|
|
mag <- find (isammolink . (^. dtValue . _2)) (itmtree ^. dtLeft)
|
|
i <- mag ^. dtValue . _1 . itConsumables
|
|
guard $ crIsAiming cr
|
|
guard $ i >= x
|
|
invid <- mag ^? dtValue . _1 . itLocation . ilInvID
|
|
return $
|
|
(cWorld . lWorld . lights .:~ LSParam pos 250 0.7)
|
|
. (cWorld . lWorld . creatures . ix (_crID cr) . crInv . ix invid . itConsumables . _Just -~ x)
|
|
where
|
|
itmtree = loc ^. locDT
|
|
(p, q) = locOrient loc cr
|
|
x = 10
|
|
isammolink AmmoMagSF{} = True
|
|
isammolink _ = False
|
|
pos = _crPos cr `v2z` 0 + rotate3 cdir (p + Q.rotate q (V3 8 0 1.5))
|
|
cdir = _crDir cr
|
|
|
|
-- this probably needs to be set to null when dropped as well?
|
|
-- does this need to be updated if it is not attached to the used root item?
|
|
updateItemTargeting :: TargetingType -> Creature -> Item -> World -> World
|
|
updateItemTargeting tt cr itm w = case tt of
|
|
_
|
|
| not isattached ->
|
|
w
|
|
& pointittarg . itTgPos .~ Nothing
|
|
& pointittarg . itTgActive .~ False
|
|
TargetRBPress
|
|
| rbpressed ->
|
|
w
|
|
& pointittarg . itTgPos %~ maybe (Just $ w ^. cWorld . lWorld . lAimPos) Just
|
|
& pointittarg . itTgActive .~ True
|
|
TargetRBPress ->
|
|
w
|
|
& pointittarg . itTgPos .~ Nothing
|
|
& pointittarg . itTgActive .~ False
|
|
TargetRBCreature -> w & pointittarg %~ setRBCreatureTargeting cr w
|
|
TargetCursor ->
|
|
w
|
|
& pointittarg
|
|
.~ ItTargeting
|
|
(Just (w ^. cWorld . lWorld . lAimPos))
|
|
Nothing
|
|
True
|
|
where
|
|
pointittarg = cWorld . lWorld . creatures . ix cid . crInv . ix invid . itTargeting
|
|
cid = _crID cr
|
|
invid = _ilInvID $ _itLocation itm
|
|
isattached = itm ^?! itLocation . ilIsAttached
|
|
rbpressed = SDL.ButtonRight `M.member` _mouseButtons (_input w)
|
|
|
|
setRBCreatureTargeting :: Creature -> World -> ItemTargeting -> ItemTargeting
|
|
setRBCreatureTargeting cr w ituse
|
|
| SDL.ButtonRight `M.member` _mouseButtons (_input w)
|
|
, isJust (ituse ^? itTgID . _Just)
|
|
, canSeeTarget =
|
|
ituse & updatePos & itTgActive .~ True
|
|
| otherwise = ituse & itTgID .~ fmap _crID newtarg & updatePos & itTgActive .~ False
|
|
where
|
|
newtarg =
|
|
safeMinimumOn (dist mwp . _crPos)
|
|
. filter (canseepos . _crPos)
|
|
$ crsNearCirc mwp 40 w
|
|
canseepos p = hasLOS (_crPos cr) p w
|
|
mwp = w ^. cWorld . lWorld . lAimPos
|
|
updatePos t' = t' & itTgPos .~ posFromMaybeID (_itTgID t')
|
|
posFromMaybeID Nothing = Nothing
|
|
posFromMaybeID (Just i) = w ^? cWorld . lWorld . creatures . ix i . crPos
|
|
canSeeTarget = fromMaybe False $ do
|
|
cid <- ituse ^? itTgID . _Just
|
|
cpos <- w ^? cWorld . lWorld . creatures . ix cid . crPos
|
|
Just $ hasLOS cpos (_crPos cr) w
|
|
|
|
--isFrictionless :: Creature -> Bool
|
|
--isFrictionless cr = case cr ^? crStance . carriage of
|
|
-- Just (Boosting _) -> True
|
|
-- Just Floating -> True
|
|
-- _ -> False
|