module Dodge.Creature.ReaderUpdate ( doStrategyActions, setTargetMv, targetYouWhenCognizant, overrideMeleeCloseTarget, watchUpdateStrat, overrideInternal, searchIfDamaged, goToTarget, flockACC, chaseCritMv, setMvPos, setViewPos, ) where import Control.Applicative import Control.Monad import Data.Bifunctor import Data.List (sortOn) import Data.Maybe import Dodge.Base import Dodge.Creature.Perception import Dodge.Creature.Radius import Dodge.Creature.Vocalization import Dodge.Data.CreatureEffect import Dodge.Data.World import Dodge.Zoning.Creature import FoldableHelp import Geometry import qualified IntMapHelp as IM import LensHelp import Linear import RandomHelp overrideMeleeCloseTarget :: World -> Creature -> Creature overrideMeleeCloseTarget w cr = maybe cr (tryMeleeAttack cr) $ do i <- _targetCr $ _crIntention cr w ^? cWorld . lWorld . creatures . ix i tryMeleeAttack :: Creature -> Creature -> Creature tryMeleeAttack cr tcr | _meleeCooldown (_crType cr) == 0 && Just (_crID tcr) == cr ^? crActionPlan . apStrategy . meleeTarget && dist tpos cpos < crRad (cr ^. crType) + crRad (tcr ^. crType) + 5 && abs (_crDir cr - argV (tpos -.- cpos)) < pi / 4 = cr & crActionPlan . apImpulse .~ [Melee $ _crID tcr] & crActionPlan . apAction .~ [ DoReplicate 10 NoAction `DoActionThen` DoImpulses [ChangeStrategy (CloseToMelee $ _crID tcr)] ] & crActionPlan . apStrategy .~ MeleeStrike | otherwise = cr where cpos = cr ^. crPos . _xy tpos = tcr ^. crPos . _xy setMvPos :: World -> Creature -> Creature setMvPos w cr = cr & crIntention . mvToPoint .~ mpos where int = _crIntention cr mtpos = do i <- int ^. targetCr tpos <- w ^? cWorld . lWorld . creatures . ix i . crPos . _xy guard $ hasLOSIndirect (cr ^. crPos . _xy) tpos w return tpos mpos = mtpos <|> _mvToPoint int setViewPos :: World -> Creature -> Creature setViewPos w cr = cr & crIntention . viewPoint %~ ((<|> mpos) . (attentionViewPoint w cr <|>)) where mpos = listToMaybe (_soundsToInvestigate $ _crMemory cr) attentionViewPoint :: World -> Creature -> Maybe Point2 attentionViewPoint w cr = do attention <- cr ^? crPerception . cpAttention . getAttentiveTo cid <- sortOn snd (IM.toList attention) ^? ix 0 . _1 tcr <- w ^? cWorld . lWorld . creatures . ix cid guard $ visionCheck cr (tcr ^. crPos . _xy) > 0 tcr ^? crPos . _xy setTargetMv :: -- | Function for determining target (World -> Creature -> Maybe Creature) -> World -> Creature -> Creature setTargetMv targFunc w cr = maybe cr (\ctarg -> cr & crIntention . mvToPoint ?~ (ctarg ^. crPos . _xy)) (targFunc w cr) -- ugly flockACC :: World -> Creature -> Creature flockACC w cr = fromMaybe cr $ do i <- cr ^. crIntention . targetCr tcr <- w ^? cWorld . lWorld . creatures . ix i let tpos = tcr ^. crPos . _xy cpos = cr ^. crPos . _xy isFarACC cr' = _crGroup cr' == _crGroup cr && _crID cr' /= _crID cr && dist (cr' ^. crPos . _xy) tpos > dist cpos tpos macr = safeMinimumOn (dist cpos . (^. crPos . _xy)) . filter isFarACC $ crsNearCirc cpos 50 w return $ case macr of Nothing -> cr Just acr -> let r = crRad (acr ^. crType) + crRad (cr ^. crType) + 10 horDir = normalizeV (vNormal (cpos -.- tpos)) horShift = if isLHS tpos cpos (acr ^. crPos . _xy) then r *.* horDir else negate r *.* horDir in cr & crIntention . mvToPoint ?~ tpos +.+ horShift chaseCritMv :: World -> Creature -> Creature chaseCritMv w cr = case _apStrategy (_crActionPlan cr) of StrategyActions _ _ -> cr WarningCry -> cr MeleeStrike -> cr CloseToMelee cid | Just 0 == (cr ^? crVocalization . vcCoolDown) -> cr & crActionPlan . apAction .:~ ImpulsesList ( [Bark soundid] : replicate numjits [RandomImpulse thejitter] ++ [[ChangeStrategy (CloseToMelee cid)]] ) & crVocalization . vcCoolDown .~ 10 & crActionPlan . apStrategy .~ WarningCry where thejitter = RandImpulseCircMove 3 soundid = evalState (takeOne (crWarningSounds cr)) (_randGen w) numjits = fst $ randomR (15, 25) (_randGen w) _ -> case cr ^? crIntention . mvToPoint . _Just of Just p | dist (cr ^. crPos . _xy) p > crRad (cr ^. crType) -> cr & crActionPlan . apAction .~ [PathTo p] | otherwise -> cr & crActionPlan . apAction .~ [bfsThenReturn 500 `DoActionThen` DoImpulses [ChangeStrategy WatchAndWait]] & crActionPlan . apStrategy .~ WatchAndWait & crIntention . mvToPoint .~ Nothing _ -> viewTarget w cr goToTarget :: World -> Creature -> Creature goToTarget w cr = case cr ^? crIntention . mvToPoint . _Just of Just p -> cr & crActionPlan . apAction .~ [PathTo p] _ -> viewTarget w cr viewTarget :: World -> Creature -> Creature viewTarget w cr = case cr ^? crIntention . viewPoint . _Just of Just p | hasLOSIndirect p (cr ^. crPos . _xy) w -> cr & crActionPlan . apAction .~ [TurnToPoint p] & crIntention . viewPoint .~ Nothing & crActionPlan . apStrategy .~ Investigate | otherwise -> cr & crActionPlan . apAction %~ replaceNullWith (PathTo p) & crActionPlan . apStrategy .~ Investigate Nothing -> cr replaceNullWith :: a -> [a] -> [a] replaceNullWith x [] = [x] replaceNullWith _ xs = xs doStrategyActions :: Creature -> Creature doStrategyActions cr = case cr ^? crActionPlan . apStrategy of Just (StrategyActions strat acs) -> cr & crActionPlan . apAction .~ acs & crActionPlan . apStrategy .~ strat _ -> cr overrideInternal :: (Creature -> Bool) -> (Creature -> Creature) -> Creature -> Creature overrideInternal test update cr | test cr = update cr | otherwise = cr watchUpdateStrat :: [(World -> Creature -> Bool, World -> Creature -> Strategy)] -> World -> Creature -> Creature watchUpdateStrat fs w cr = case cr ^? crActionPlan . apStrategy of Just WatchAndWait -> cr & crActionPlan . apStrategy .~ listGuard (map (first uncurry) fs, \_ _ -> WatchAndWait) (w, cr) w cr _ -> cr listGuard :: ([(a -> Bool, b)], b) -> a -> b listGuard ((test, y) : ps, z) x | test x = y | otherwise = listGuard (ps, z) x listGuard (_, z) _ = z targetYouWhenCognizant :: World -> Creature -> Creature targetYouWhenCognizant w cr = case cr ^? crPerception . cpAwareness . ix 0 of -- so this caused a space leak: be careful with ?~ -- consider changing targeted creature to be just an index Just (Cognizant _) -> (w ^?! cWorld . lWorld . creatures . ix 0) `seq` cr & crIntention . targetCr ?~ 0 & crPerception . cpVigilance .~ Vigilant _ -> cr & crIntention . targetCr .~ Nothing searchIfDamaged :: Creature -> Creature searchIfDamaged cr | _crPain cr > 0 && _apStrategy (_crActionPlan cr) == WatchAndWait = cr & crPerception . cpVigilance .~ Vigilant & crActionPlan . apStrategy .~ StrategyActions LookAround [ TurnToPoint (cr ^. crPos . _xy -.- unitVectorAtAngle (_crDir cr)) `DoActionThen` 40 `WaitThen` bfsThenReturn 500 ] | otherwise = cr bfsThenReturn :: Int -> Action bfsThenReturn t = ArbitraryAction (CrWdBFSThenReturn t) -- theaction -- where -- theaction cr w = fromMaybe NoAction $ do -- n <- walkableNodeNear w (_crPos cr) -- let as = take 20 $ map PathTo $ bfsNodePoints n w -- return $ DoReplicate t $ -- foldr DoActionThen NoAction as