{- | Module testing for properties of creatures, whether they are reloading, etc. Each function takes a creature, and possibly other parameters, and returns a bool. Note that if there is a world parameter, the creature NEED NOT be the same as the creature with that id in the world, in fact a creature with that id need not exist. -} module Dodge.Creature.Test ( crIsAiming, crIsReloading, crIsArmouredFrom, oneH, twists, twoFlat, crWeaponReady, crInAimStance, crNearPoint, isAnimate, crCanShoot, crHasTargetLOS, crAwayFromPost, crHasTarget, crStratConMatches, crSafeDistFromTarg, ) where import Control.Lens import Data.List (find) import Data.Maybe import Dodge.Base.Collide import Dodge.Data.World import Geometry crIsReloading :: Creature -> Bool crIsReloading cr = case cr ^? crInvSel . iselAction of Just ReloadAction{} -> True _ -> False crWeaponReady :: Creature -> Bool crWeaponReady cr = fromMaybe False $ do ic <- cr ^? crInv . ix (crSel cr) . itUse . heldConsumption return (_laLoaded ic > 0 && _laPrimed ic) crCanSeeCr :: Creature -> (World, Creature) -> Bool crCanSeeCr tcr (w, cr) = hasLOS (_crPos cr) (_crPos tcr) w crIsAiming :: Creature -> Bool crIsAiming cr = _posture (_crStance cr) == Aiming crHasTarget :: Creature -> Bool crHasTarget cr = isJust $ cr ^? crIntention . targetCr . _Just crHasTargetLOS :: World -> Creature -> Bool crHasTargetLOS w cr = case cr ^? crIntention . targetCr . _Just of Just i -> crCanSeeCr i (w, cr) Nothing -> False crSafeDistFromTarg :: Float -> Creature -> Bool crSafeDistFromTarg d cr = case cr ^? crIntention . targetCr . _Just of Just tcr -> dist (_crPos cr) (_crPos tcr) > d Nothing -> True crStratConMatches :: Strategy -> Creature -> Bool crStratConMatches strat cr = strat == _apStrategy (_crActionPlan cr) -- this equality check might be slow... crAwayFromPost :: Creature -> Bool crAwayFromPost cr = case find sentinelGoal . _apGoal $ _crActionPlan cr of Just (SentinelAt p _) -> dist p (_crPos cr) > 15 _ -> False where sentinelGoal (SentinelAt _ _) = True sentinelGoal _ = False crCanShoot :: Creature -> Bool crCanShoot cr = crIsAiming cr && crWeaponReady cr crInAimStance :: AimStance -> Creature -> Bool crInAimStance as cr = crIsAiming cr && cr ^? crInv . ix (crSel cr) . itUse . heldAim . aimStance == Just as oneH :: Creature -> Bool oneH = crInAimStance OneHand twoFlat :: Creature -> Bool twoFlat = crInAimStance TwoHandFlat twists :: Creature -> Bool twists = crInAimStance TwoHandTwist -- the use of crOldPos is because the damage position is calculated on the -- previous frame -- Not sure if it is a good idea crIsArmouredFrom :: Point2 -> Creature -> Bool crIsArmouredFrom = hasFrontArmour hasFrontArmour :: Point2 -> Creature -> Bool hasFrontArmour p cr = fromMaybe False $ do invid <- cr ^? crEquipment . ix OnChest ittype <- cr ^? crInv . ix invid . itType . iyBase return $ EQUIP FRONTARMOUR == ittype && p /= _crOldPos cr && angleVV (unitVectorAtAngle (_crDir cr + frontarmdirection)) (p -.- _crOldPos cr) < pi / 2 where -- even though angleVV can generate NaN, the comparison seems to deal with it frontarmdirection | crInAimStance OneHand cr = 0.5 | crInAimStance TwoHandTwist cr = negate 1 | otherwise = 0 --crOnSeg :: Point2 -> Point2 -> Creature -> Bool --crOnSeg p1 p2 cr = circOnSeg p1 p2 (_crPos cr) (_crRad cr) --crNearSeg :: Float -> Point2 -> Point2 -> Creature -> Bool --crNearSeg d p1 p2 cr = circOnSeg p1 p2 (_crPos cr) (_crRad cr + d) crNearPoint :: Float -> Point2 -> Creature -> Bool crNearPoint d p cr = dist (_crPos cr) p < d + _crRad cr isAnimate :: Creature -> Bool {-# INLINE isAnimate #-} isAnimate cr = case _crActionPlan cr of Inanimate -> False _ -> True