module Dodge.Creature.State ( stateUpdate , doDamage ) where import Dodge.Data import Dodge.Creature.State.WalkCycle --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 StrictHelp 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 import Data.Monoid import qualified Data.Vector as V import qualified Data.Vector.Mutable as MV --dummyUpdate :: CRUpdate --dummyUpdate cr _ = (Endo id, Just cr) -- | The movement is 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? -- currently invSideEffects are applied after the creature moves, to correctly -- update shield positions stateUpdate :: CRUpdate -> CRUpdate stateUpdate u cr w = case u (updateMovement cr) w of (f, maybeCr) -> ( Endo $ invSideEff (fromMaybe cr maybeCr) . movementSideEff cr . deathEff . appEndo f , 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) 0) . over decorations addCorpse . dropByState cr addCorpse = IM.insertNewKey $ uncurryV translate (_crOldPos cr) $ rotate (_crDir cr) (_crCorpse cr) -- | Drop items according to the creature state. dropByState :: Creature -> World -> World dropByState cr w = foldr (copyInvItemToFloor 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 = cr & crOldPos .~ _crPos cr & crOldDir .~ _crDir cr --meleeCooldown :: CRUpdate -> CRUpdate --meleeCooldown u cr = u $ cr & crMeleeCooldown . _Just %~ (max 0 . (\x -> x - 1)) doDamage :: Creature -> Creature doDamage cr = set (crState . crDamage) [] . over (crState . crPastDamage) (advancePastDamages dams) $ snd $ _crApplyDamage cr dams cr where dams = _crDamage $ _crState cr advancePastDamages :: [DamageType] -> V.Vector [DamageType] -> V.Vector [DamageType] advancePastDamages newDs v = f $ V.backpermute v bpVector where f = V.modify $ \v' -> MV.write v' 0 newDs bpVector :: V.Vector Int bpVector = V.fromList $ 0 : [0 .. 18] 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)) 20 (momentum +.+ 1 *.* rotateV randDir (vInverse v)) Nothing 1 20 $ set randGen g w _ -> w | otherwise = footstepSideEffect cr 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 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 (_itEffect it) cr i w' weaponReloadSounds :: Creature -> World -> World weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of Just Weapon{_wpReloadType = PassiveReload stype,_wpReloadTime=rt,_wpReloadState=rs} | rt == rs -> soundContinue (CrReloadSound 0) (_crPos cr) stype Nothing w | otherwise -> w Just Weapon{_wpReloadState = 0} -> w Just Weapon{_wpReloadState = 1} -> stopSoundFrom (CrReloadSound cid) w Just Weapon{} -> soundContinue (CrReloadSound cid) (_crPos cr) reloadS (Just 1) w _ -> w where cid = _crID cr updateMovement :: Creature -> Creature updateMovement cr | isFrictionless cr = over crPos (+.+ momentum) $ setOldPos cr | otherwise = updateWalkCycle $ setOldPos cr where momentum' = 0.98 *.* (_crPos cr -.- _crOldPos cr) momentum'' | magV momentum' > 1 = 1 *.* normalizeV momentum' | otherwise = momentum' momentum = momentum'' isFrictionless :: Creature -> Bool isFrictionless cr = case cr ^? crStance . carriage of Just (Boosting _) -> True Just Floating -> True _ -> False stepItemUseCooldown :: Creature -> Creature stepItemUseCooldown cr = cr & crInv . ix iSel %~ (itUseTime %~ decreaseToZero) . (wpCurWarmUp %~ decreaseToZero) where iSel = _crInvSel cr stepReloading :: Creature -> Creature stepReloading cr = over (crInv . ix iSel . wpReloadState) decreaseToZero cr where iSel = _crInvSel cr decreaseToZero :: Int -> Int decreaseToZero x = max 0 (x - 1)