{- | 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 Dodge.Data import Dodge.Base.Collide import Geometry import SameConstr import Data.List (find) import Data.Maybe --import qualified Data.IntMap.Strict as IM import Control.Lens 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) . itConsumption 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 = eqConstr strat (_apStrategy $ _crActionPlan cr) 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 . useAim . 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 $ FRONTARMOUR == ittype && p /= _crOldPos cr && angleVV (unitVectorAtAngle (_crDir cr + frontarmdirection)) (p -.- _crOldPos cr) < pi/2 -- even though angleVV can generate NaN, the comparison seems to deal with it where 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