154 lines
5.9 KiB
Haskell
154 lines
5.9 KiB
Haskell
--{-# LANGUAGE TupleSections #-}
|
|
module Dodge.Creature.Perception
|
|
( perceptionUpdate
|
|
, chaseCritPerceptionUpdate
|
|
, newSounds
|
|
) where
|
|
import Dodge.Data
|
|
import Dodge.Creature.Vocalization
|
|
import Dodge.Base.Collide
|
|
import Dodge.RandomHelp
|
|
--import Dodge.SoundLogic
|
|
import Geometry.Vector
|
|
--import Geometry.Data
|
|
import Geometry.Data
|
|
import Sound.Data
|
|
--import StrictHelp
|
|
|
|
import Control.Monad.State
|
|
import Data.Maybe
|
|
import Control.Lens
|
|
import qualified Data.IntMap.Strict as IM
|
|
import qualified Data.Map.Strict as M
|
|
|
|
perceptionUpdate
|
|
:: [Int] -- ^ List of creature ids that may direct attention and awareness
|
|
-> World
|
|
-> Creature
|
|
-> Creature
|
|
perceptionUpdate is w = rememberSounds w . basicAwarenessUpdate . basicAttentionUpdate is w
|
|
|
|
chaseCritPerceptionUpdate :: [Int] -> World -> Creature -> Creature
|
|
chaseCritPerceptionUpdate is w =
|
|
rememberSounds w . chaseCritAwarenessUpdate w . basicAttentionUpdate is w
|
|
|
|
{- | Update a creatures awareness based upon the creatures' current direction
|
|
of attention -}
|
|
basicAwarenessUpdate :: Creature -> Creature
|
|
basicAwarenessUpdate cr = case _crAttentionDir $ _crPerception cr of
|
|
Fixated i -> cr & crPerception . crAwarenessLevel
|
|
%~ (IM.insert i (Cognizant 10000) . IM.mapMaybe decreaseAwareness)
|
|
AttentiveTo is -> cr
|
|
& crPerception . crAwarenessLevel
|
|
%~ (IM.mapMaybe decreaseAwareness . IM.unionWith combineAwareness is)
|
|
& maybeBark
|
|
where
|
|
oldAwareness = _crAwarenessLevel $ _crPerception cr
|
|
newAwareness = (IM.mapMaybe decreaseAwareness . IM.unionWith combineAwareness is) oldAwareness
|
|
becomesCognizant = any isCognizant $ IM.unionWith cogRaised oldAwareness newAwareness
|
|
maybeBark | becomesCognizant = case vocalizationTest cr of
|
|
Just sid -> crActionPlan . crImpulse .~ [Bark sid]
|
|
Nothing -> id
|
|
| otherwise = id
|
|
|
|
chaseCritAwarenessUpdate :: World -> Creature -> Creature
|
|
chaseCritAwarenessUpdate w cr = case _crAttentionDir $ _crPerception cr of
|
|
Fixated i -> cr & crPerception . crAwarenessLevel
|
|
%~ (IM.insert i (Cognizant 10000) . IM.mapMaybe decreaseAwareness)
|
|
AttentiveTo is -> cr
|
|
& crPerception . crAwarenessLevel
|
|
%~ (IM.mapMaybe decreaseAwareness . IM.unionWith combineAwareness is)
|
|
& maybeBark
|
|
where
|
|
oldAwareness = _crAwarenessLevel $ _crPerception cr
|
|
newAwareness = (IM.mapMaybe decreaseAwareness . IM.unionWith combineAwareness is) oldAwareness
|
|
becomesCognizant = any isCognizant $ IM.unionWith cogRaised oldAwareness newAwareness
|
|
randBool = takeOne [False,True] & evalState $ _randGen w
|
|
maybeBark
|
|
| becomesCognizant && randBool = case vocalizationTest cr of
|
|
Just soundid -> crActionPlan . crStrategy .~ StrategyActions WarningCry
|
|
[DoImpulses [Bark soundid]
|
|
`DoActionThen` 20
|
|
`WaitThen` DoImpulses [ChangeStrategy $ CloseToMelee 0]
|
|
, AimAt 0 (_crPos $ _creatures w IM.! 0)
|
|
]
|
|
Nothing -> id
|
|
| otherwise = id
|
|
|
|
cogRaised :: AwarenessLevel -> AwarenessLevel -> AwarenessLevel
|
|
cogRaised Suspicious{} Cognizant{} = Cognizant 100
|
|
cogRaised _ _ = Suspicious 0
|
|
|
|
isCognizant :: AwarenessLevel -> Bool
|
|
isCognizant Cognizant {} = True
|
|
isCognizant _ = False
|
|
|
|
combineAwareness :: AwarenessLevel -> AwarenessLevel -> AwarenessLevel
|
|
combineAwareness (Suspicious x) (Suspicious y)
|
|
| x + y < 5000 = Suspicious $ x + y
|
|
| otherwise = Cognizant 1000
|
|
combineAwareness (Suspicious x) (Cognizant y) = Cognizant $ min 10000 $ x + y
|
|
combineAwareness (Cognizant x) (Suspicious y) = Cognizant $ min 10000 $ x + y
|
|
combineAwareness (Cognizant x) (Cognizant y) = Cognizant $ min 10000 $ x + y
|
|
|
|
{- | Decrease awareness level. Returns 'Maybe' value for use with 'IM.mapMaybe'
|
|
-}
|
|
decreaseAwareness :: AwarenessLevel -> Maybe AwarenessLevel
|
|
decreaseAwareness (Suspicious 0) = Nothing
|
|
decreaseAwareness (Suspicious x) = Just $ Suspicious (x - 50)
|
|
decreaseAwareness (Cognizant 0) = Just $ Suspicious 1000
|
|
decreaseAwareness (Cognizant x) = Just $ Cognizant $ x - 50
|
|
|
|
{- | Given a fixed group of creatures, direct attention to those of them that
|
|
- are in view. -}
|
|
basicAttentionUpdate
|
|
:: [Int] -- ^ Creatures that may attract this creature's attention
|
|
-> World
|
|
-> Creature
|
|
-> Creature
|
|
basicAttentionUpdate cids w cr = cr & crPerception . crAttentionDir .~
|
|
AttentiveTo (IM.mapMaybe (newExtraAwareness cr w) $ IM.fromList $ zip cids cids)
|
|
|
|
newExtraAwareness
|
|
:: Creature -- ^ source creature
|
|
-> World
|
|
-> Int -- ^ target creature id
|
|
-> Maybe AwarenessLevel
|
|
newExtraAwareness cr w cid
|
|
| not $ canSeeIndirect (_crID cr) cid w = Nothing
|
|
| otherwise = Just . Suspicious $ _viFOV vi ang * _viDist vi d * awakeLevelPerception cr
|
|
where
|
|
vi = _crVision $ _crPerception cr
|
|
tpos = _crPos $ _creatures w IM.! cid
|
|
cpos = _crPos cr
|
|
ang = angleVV (unitVectorAtAngle (_crDir cr)) (tpos - cpos)
|
|
d = dist tpos cpos
|
|
|
|
awakeLevelPerception :: Creature -> Float
|
|
awakeLevelPerception cr = case _crAwakeLevel $ _crPerception cr of
|
|
Comatose -> 0
|
|
Asleep -> 10
|
|
Lethargic -> 100
|
|
Vigilant -> 1000
|
|
Overstrung -> 10000
|
|
|
|
newSounds :: World -> [(Point2,Float)]
|
|
newSounds = mapMaybe f . M.elems . _playingSounds
|
|
where
|
|
f s = case _playStatus $ _soundStatus s of
|
|
JustStartedPlaying -> Just (_soundPos s, _soundVolume s)
|
|
_ -> Nothing
|
|
|
|
rememberSounds :: World -> Creature -> Creature
|
|
rememberSounds w cr = cr
|
|
& crMemory . soundsToInvestigate .~ closesounds
|
|
& awakeupdate
|
|
where
|
|
awakeupdate | null closesounds = id
|
|
| otherwise = crPerception . crAwakeLevel .~ Vigilant
|
|
closesounds = map fst (filter (soundIsClose cr) (newSounds w))
|
|
|
|
|
|
soundIsClose :: Creature -> (Point2,Float) -> Bool
|
|
soundIsClose cr (pos,vol) = vol > (_auDist . _crAudition $ _crPerception cr) (dist pos (_crPos cr))
|