Files
loop/src/Dodge/Creature/State.hs
T
2026-03-26 23:35:12 +00:00

317 lines
12 KiB
Haskell

module Dodge.Creature.State (
-- crUpdate,
doDamage,
invItemEffs,
) where
import qualified Data.IntMap.Strict as IM
import Linear
import NewInt
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.Creature.Damage
import Dodge.Creature.Impulse.Movement
import Dodge.Creature.Test
import Dodge.Data.ComposedItem
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.SoundLogic
import Dodge.Targeting.Draw
import Dodge.Zoning.Creature
import FoldableHelp
import Geometry
import LensHelp
import qualified Quaternion as Q
import RandomHelp
import qualified SDL
doDamage :: Int -> World -> World
doDamage cid w = fromMaybe w $ do
cr <- w ^? cWorld . lWorld . creatures . ix cid
return $ applyPastDamages cr $ applyCreatureDamage (cr ^. crDamage) cr w
-- 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 (w ^. cWorld . lWorld)
& cWorld . lWorld . creatures . ix (_crID cr) . crPain -~ y
& randGen .~ g
-- a loop going over all root inventory items
invItemEffs :: Int -> World -> World
invItemEffs cid w = fromMaybe w $ do
cr <- w ^? cWorld . lWorld . creatures . ix cid
return . appEndo (
foldMap
(reduceLocDT (Endo . invItemLocUpdate cr) . LocDT TopDT)
(invDT' $ fmap (\k -> w ^?! cWorld . lWorld . items . ix k) (_crInv cr))) $ w
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 -> rootAndAttNotEff createShieldWall removeShieldWall itm cr w
HELD MINIGUNX{} -> coolMinigun itm w
HELD MACHINEPISTOL{} -> coolMachinePistol cr itm w
LASER | loc ^. locDT . dtValue . _2 == WeaponTargetingSF
, itm ^? itLocation . ilIsAttached == Just True -> shineTargetLaser cr loc w
HELD LED
| 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))
(cr ^. crPos . _xy)
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 (_unNInt invid) cr
itm' <- cr ^? crInv . ix (NInt i) >>= \k -> w ^? cWorld . lWorld . items . ix k
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 ^? itID . unNInt
x <- loc ^? locDT . dtValue . _1 . itConsumables . _Just
guard $ x < 200
bat <- loc ^? locDT . dtLeft . ix 0 . dtValue . _1
j <- bat ^? itID . unNInt
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 . items
trySynthBullet :: LocationDT OItem -> World -> World
trySynthBullet loc w = fromMaybe w $ do
i <- itm ^? itID . unNInt
x <- itm ^? itUse . uaParams . apInt
if x < 100
then do
bat <- loc ^? locDT . dtLeft . ix 0 . dtValue . _1
j <- bat ^? itID . unNInt
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 ^? itID . unNInt
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 . items
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
magitid <- mag ^? dtValue . _1 . itID . unNInt
return $
w
& worldEventFlags . at InventoryChange ?~ ()
& cWorld . lWorld . items
. ix magitid
. 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 = cr ^. crPos . _xy + xyV3 (rotate3z cdir p)
cdir = _crDir cr
itm = itmtree ^. dtValue . _1
pointittarg = cWorld . lWorld . items . ix itid . itTargeting
itid = itm ^. itID . unNInt
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
itid <- mag ^? dtValue . _1 . itID . unNInt
return $
(cWorld . lWorld . lights .:~ LSParam pos 150 0.3)
. (cWorld . lWorld . lights .:~ LSParam (pos + V3 0 0 15) 50 0.3)
. (cWorld . lWorld . items . ix itid . itConsumables . _Just -~ x)
where
itmtree = loc ^. locDT
(p, q) = locOrient loc cr
x = 10
isammolink AmmoMagSF{} = True
isammolink _ = False
pos = _crPos cr + rotate3z cdir (p + Q.rotate q (V3 5 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 . items . ix itid . itTargeting
itid = itm ^. itID . unNInt
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 . _xy))
. filter (canseepos . (^. crPos . _xy))
. IM.elems
$ crsNearCirc mwp 40 w
canseepos p = hasLOS ((^. crPos . _xy) 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 . _xy
canSeeTarget = fromMaybe False $ do
cid <- ituse ^? itTgID . _Just
cpos <- w ^? cWorld . lWorld . creatures . ix cid . crPos . _xy
Just $ hasLOS cpos ( cr ^. crPos . _xy) w
--isFrictionless :: Creature -> Bool
--isFrictionless cr = case cr ^? crStance . carriage of
-- Just (Boosting _) -> True
-- Just Floating -> True
-- _ -> False