Refactor ai

This commit is contained in:
jgk
2021-05-12 14:24:31 +02:00
parent 86faf9fd01
commit ead87af3c1
25 changed files with 718 additions and 453 deletions
+5 -213
View File
@@ -1,17 +1,15 @@
module Dodge.AIs
( basicShooterAI
, twitchMissAI
( twitchMissAI
, launcherAI
, chargeAI
, sniperAI
, dodgeAI
, miniAI
, spawnerAI
) where
import Dodge.Creature.Stance.Data
import Dodge.Creature.State.Data
import Dodge.Creature.Impulse.Data
import Dodge.Data
import Dodge.Data hiding (Action (..), Impulse (..), Strategy (..))
import Dodge.Base
import Dodge.Path
import Dodge.SoundLogic
@@ -64,7 +62,7 @@ addImpulse newActions = over (crState . goals) (\(as:ass) -> (newActions++as):as
factionIs :: Faction -> Creature -> Bool
factionIs f c = (_faction $ _crState $ c) == f
factionIs f c = (_crFaction $ c) == f
lineOrth :: Point2 -> IM.IntMap Creature -> Point2 -> Point2
lineOrth ypos crs cpos = p
@@ -228,7 +226,7 @@ spawnerAI spawn w (f,g) cr =
addImpulse newActions
= Just . over (crState . goals) (\(as:ass) -> (newActions++as):ass)
needChild = length (filter (\cr' -> (cr' ^? crState . faction) == Just (SpawnedBy cid))
needChild = length (filter (\cr' -> (cr' ^? crFaction) == Just (SpawnedBy cid))
$ IM.elems $ _creatures w)
< 15
spawnChild = placeCrFaction spawn (SpawnedBy cid) (cpos +.+ rotateV dir (1,0)) cpos (_crDir cr)
@@ -248,117 +246,11 @@ spawnerAI spawn w (f,g) cr =
placeCrFaction :: Creature -> Faction -> Point2 -> Point2 -> Float -> World -> World
placeCrFaction cr fact p op rot w = over creatures addCr w
where addCr crs = IM.insert (Dodge.Base.newKey crs)
( set (crState . faction) fact
( set crFaction fact
$ cr {_crPos = p,_crOldPos = op,_crDir = rot,_crID = Dodge.Base.newKey crs}
)
crs
-- be aware that the crit can be removed -- don't just refer to its id in the
-- world->world part
miniAI :: World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
miniAI w (f,g) cr =
let cpos = _crPos cr
cid = _crID cr
ypos = _crPos $ you w
wp = _crInv cr IM.! _crInvSel cr
(aimtime,g1) = randomR (80,120) g
noAmmo = 0 == _wpLoadedAmmo wp
turnCloseSlow p | errorAngleVV 22 (p -.- cpos)
(unitVectorAtAngle (_crDir cr)) < 0.4
= crTurnTowardSpeed 0.01 p
| otherwise = crTurnTowardSpeed 0.05 p
replaceGoal newImpulses = Just . over (crState . goals) (\gs -> newImpulses ++ tail gs)
addGoal newImpulses = Just . over (crState . goals) (\gs -> newImpulses ++ gs)
replaceImpulse newActions
= Just . over (crState . goals) (\((_:as):ass) -> (newActions ++ as):ass)
addImpulse newActions
= Just . over (crState . goals) (\(as:ass) -> (newActions++as):ass)
-- autoFireTweak p | _wpIsAuto wp = Just
-- | otherwise = replaceGoal [[AimAt p (_itUseRate wp * 2)]]
in --if _crHP cr <= 0 then killCr w f cr else
case head $ _goals $ _crState cr of
[] | canSeeFire cpos ypos w -> ( ( f , g1 )
, replaceGoal [[AimAt ypos aimtime]] cr
)
| otherwise -> ( ( f , g )
, replaceGoal [] cr
)
(WaitFor x:gls)| canSeeFire cpos ypos w -> ( ( f , g1 )
, replaceGoal [[AimAt ypos aimtime]] cr
)
| x == 0 -> ( (f ,g ), replaceImpulse [] cr )
| otherwise -> ( (f ,g ), replaceImpulse [WaitFor (x-1)] cr )
(MoveToFor p x:gls)
| canSeeFire cpos ypos w -> ( (f , g1)
, replaceGoal [[AimAt ypos aimtime]] cr
)
| x == 0 -> ( (f , g)
, replaceImpulse [] cr
)
| dist p cpos < 10 -> ( (f , g) , replaceImpulse [] cr)
| angleVV (unitVectorAtAngle (_crDir cr)) (p -.- cpos) > pi/4
-> ( (f , g)
, replaceImpulse [MoveToFor p (x-1)] $ crTurnTowardSpeed 0.05 p cr )
| otherwise -> ( (f , g)
, replaceImpulse [MoveToFor p (x-1)] $ crMvForward 2
$ crTurnTowardSpeed 0.05 p cr )
(PathTo p:_) | dist p cpos < 10 -> ( (f , g) , replaceImpulse [] cr )
| hasLOS cpos p w -> ( (f , g) , addImpulse [MoveToFor p 100] cr )
| otherwise -> case pointTowardsImpulse cpos p w of
Just q -> ( (f , g) , addImpulse [MoveToFor q 100] cr )
_ -> ( (f , g) , replaceImpulse [] cr )
(FireAt p:gls) | noAmmo -> ( (tryUseItem cid . f , g )
, replaceGoal [[Reload
,PathTo p
,WaitFor 50
--,Search 1 ,WaitFor 100
]]
cr
)
| canSeeFireVisionAll cid (_yourID w) w
-> ( (tryUseItem cid . f , g)
, replaceGoal [[FireAt ypos]] $ turnCloseSlow p cr
)
| pathToPointFireable cid p w
-> ( (tryUseItem cid . f , g)
, Just $ turnCloseSlow p cr
)
| otherwise -> ( (f , g)
, replaceGoal [[PathTo p,WaitFor 50 ]] cr
)
(Reload:gls) | _wpReloadState wp == 0 -> ( (f , g) , replaceImpulse [] cr )
| canSeeFireVisionAny cid (_yourID w) w
-> ( (f , g)
, Just $ turnCloseSlow ypos cr
) -- no longer chase if see you when reloading
-- $ replaceImpulse [] w
| otherwise -> ( (f , g) , Just cr)
(Search i:gls) | i == 0 -> ( (f , g) , replaceImpulse [] cr)
| otherwise
-> ( ( f , g1)
, replaceImpulse [ PathTo (randomPointXStepsFrom 3 cpos w)
, Search (i-1)
] cr
)
(Guard p p':_) | dist ypos cpos < 900 && canSeeFireVisionAny cid (_yourID w) w
-> ( ( f , g1)
, addGoal [[AimAt ypos (0)]] cr
)
| dist cpos p > 10
-> ( (f,g) , addGoal [[PathTo p]] cr)
| errorAngleVV 21 (unitVectorAtAngle (_crDir cr)) (p' -.- p) > pi/8
-> ( (f,g)
,Just $ crTurnTowardSpeed 0.05 p' cr)
| otherwise -> ( (f,g) ,Just cr)
(InitGuard :_) -> ( (f,g)
,replaceGoal [[Guard cpos (cpos +.+ 50 *.* unitVectorAtAngle (_crDir cr))]] cr
)
(AimAt p i:_) | i == 0 -> ( (f,g) , replaceImpulse [FireAt p] cr )
| canSeeFireVisionAll cid (_yourID w) w
-> ( (f,g) , replaceImpulse [AimAt ypos (i-1)] $ turnCloseSlow p cr)
| otherwise
-> ( (f,g) , replaceImpulse [AimAt p (i-1)] $ turnCloseSlow p cr)
strafeOrChargeAI :: Float -> Float -> World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
strafeOrChargeAI inRange outRange w (f,g) cr =
let cpos = _crPos cr
@@ -1316,106 +1208,6 @@ suppressShooterAI w (f,g) cr =
| otherwise
-> ( (f,g) , replaceImpulse [AimAt p (i-1)] $ turnCloseSlow p cr)
basicShooterAI :: World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
basicShooterAI w (f,g) cr =
let cpos = _crPos cr
cid = _crID cr
ypos = _crPos $ you w
wp = _crInv cr IM.! _crInvSel cr
(aimtime,g1) = randomR (60,80) g
noAmmo = 0 == _wpLoadedAmmo wp
turnCloseSlow p | errorAngleVV 22 (p -.- cpos)
(unitVectorAtAngle (_crDir cr)) < 0.4
= crTurnTowardSpeed 0.01 p
| otherwise = crTurnTowardSpeed 0.05 p
replaceGoal newImpulses = Just . over (crState . goals) (\gs -> newImpulses ++ tail gs)
addGoal newImpulses = Just . over (crState . goals) (\gs -> newImpulses ++ gs)
replaceImpulse newActions
= Just . over (crState . goals) (\((_:as):ass) -> (newActions ++ as):ass)
addImpulse newActions
= Just . over (crState . goals) (\(as:ass) -> (newActions++as):ass)
in case head $ _goals $ _crState cr of
[] | canSeeFire cpos ypos w -> ( ( f , g1 )
, replaceGoal [[AimAt ypos aimtime]] cr
)
| otherwise -> ( ( f , g )
, replaceGoal [] cr
)
(WaitFor x:gls)| canSeeFire cpos ypos w -> ( ( f , g1 )
, replaceGoal [[AimAt ypos aimtime]] cr
)
| x == 0 -> ( (f ,g ), replaceImpulse [] cr )
| otherwise -> ( (f ,g ), replaceImpulse [WaitFor (x-1)] cr )
(MoveToFor p x:gls)
| canSeeFire cpos ypos w -> ( (f , g1)
, replaceGoal [[AimAt ypos aimtime]] cr
)
| x == 0 -> ( (f , g)
, replaceImpulse [] cr
)
| dist p cpos < 10 -> ( (f , g) , replaceImpulse [] cr)
| angleVV (unitVectorAtAngle (_crDir cr)) (p -.- cpos) > pi/4
-> ( (f , g)
, replaceImpulse [MoveToFor p (x-1)] $ crTurnTowardSpeed 0.05 p cr )
| otherwise -> ( (f , g)
, replaceImpulse [MoveToFor p (x-1)] $ crMvForward 2
$ crTurnTowardSpeed 0.05 p cr )
(PathTo p:_) | dist p cpos < 10 -> ( (f , g) , replaceImpulse [] cr )
| hasLOS cpos p w -> ( (f , g) , addImpulse [MoveToFor p 100] cr )
| otherwise -> case pointTowardsImpulse cpos p w of
Just q -> ( (f , g) , addImpulse [MoveToFor q 100] cr )
_ -> ( (f , g) , replaceImpulse [] cr )
(FireAt p:gls) | noAmmo -> ( (tryUseItem cid . f , g )
, replaceGoal [[Reload
,PathTo p
,WaitFor 50
--,Search 1 ,WaitFor 100
]]
cr
)
| canSeeFireVisionAll cid (_yourID w) w
-> ( (tryUseItem cid . f , g)
, replaceGoal [[FireAt ypos]] $ turnCloseSlow p cr
)
| pathToPointFireable cid p w
-> ( (tryUseItem cid . f , g)
, Just $ turnCloseSlow p cr
)
| otherwise -> ( (f , g)
, replaceGoal [[PathTo p,WaitFor 50 ]] cr
)
(Reload:gls) | _wpReloadState wp == 0 -> ( (f , g) , replaceImpulse [] cr )
| canSeeFireVisionAny cid (_yourID w) w
-> ( (f , g)
, Just $ turnCloseSlow ypos cr
) -- no longer chase if see you when reloading
-- -$ replaceImpulse [] w
| otherwise -> ( (f , g) , Just cr)
(Search i:gls) | i == 0 -> ( (f , g) , replaceImpulse [] cr)
| otherwise
-> ( ( f , g1)
, replaceImpulse [ PathTo (randomPointXStepsFrom 3 cpos w)
, Search (i-1)
] cr
)
(Guard p p':_) | dist ypos cpos < 900 && canSeeFireVisionAny cid (_yourID w) w
-> ( ( f , g1)
, addGoal [[AimAt ypos (2*aimtime)]] cr
)
| dist cpos p > 10
-> ( (f,g) , addGoal [[PathTo p]] cr)
| errorAngleVV 21 (unitVectorAtAngle (_crDir cr)) (p' -.- p) > pi/8
-> ( (f,g)
,Just $ crTurnTowardSpeed 0.05 p' cr)
| otherwise -> ( (f,g) ,Just cr)
(InitGuard :_) -> ( (f,g)
,replaceGoal [[Guard cpos (cpos +.+ 50 *.* unitVectorAtAngle (_crDir cr))]] cr
)
(AimAt p i:_) | i == 0 -> ( (f,g) , replaceImpulse [FireAt p] cr )
| canSeeFireVisionAll cid (_yourID w) w
-> ( (f,g) , replaceImpulse [AimAt ypos (i-1)] $ turnCloseSlow p cr)
| otherwise
-> ( (f,g) , replaceImpulse [AimAt p (i-1)] $ turnCloseSlow p cr)
sniperAI :: World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
sniperAI w (f,g) cr =
+11
View File
@@ -58,6 +58,11 @@ aCrPos i w = _crPos $ _creatures w IM.! i
yourItem :: World -> Item
yourItem w = _crInv (you w) IM.! _crInvSel (you w)
crItem :: World -> Int -> Item
crItem w cid = _crInv cr IM.! _crInvSel cr
where
cr = _creatures w IM.! cid
yourItemRef w = (creatures . ix (_yourID w) . crInv . ix (_crInvSel (you w)))
wallNormal :: Wall -> Point2
@@ -495,3 +500,9 @@ mvPointToward
mvPointToward !ep !p
| dist p ep < 1 = ep
| otherwise = p +.+ normalizeV (ep -.- p)
isAnimate :: Creature -> Bool
{-# INLINE isAnimate #-}
isAnimate cr = case _crActionPlan cr of
Inanimate -> False
_ -> True
+49 -22
View File
@@ -10,6 +10,8 @@ import Dodge.Creature.ImpulseRat
import Dodge.Creature.ActionRat
import Dodge.Creature.Rationality.Data
import Dodge.Creature.AlertLevel
import Dodge.Creature.SetTarget
import Dodge.Creature.Test
import Dodge.Data
import Dodge.AIs
import Dodge.Default
@@ -64,10 +66,10 @@ smallChaseCrit = defaultCreature
, _crPict = basicCrPict green
, _crState = defaultState
{_goals = [[Wait]]
,_faction = ChaseCritters
}
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 4
, _crFaction = ColorFaction green
}
chaseCrit :: Creature
chaseCrit = defaultCreature
@@ -76,10 +78,10 @@ chaseCrit = defaultCreature
, _crPict = basicCrPict green
, _crState = defaultState
{_goals = [[Wait]]
,_faction = ChaseCritters
}
, _crInv = IM.empty
, _crMeleeCooldown = Just 0
, _crFaction = ColorFaction green
}
armourChaseCrit :: Creature
armourChaseCrit = defaultCreature
@@ -92,12 +94,12 @@ armourChaseCrit = defaultCreature
,(1,medkit 200)
]
}
miniGunCrit' :: Creature
miniGunCrit' = defaultCreature
miniGunCrit :: Creature
miniGunCrit = defaultCreature
{ _crPict = basicCrPict red
, _crUpdate = stateUpdate $ impulsiveAI $ composeInternalAIs
[ actionUpdateAI performActions
, shootAtTarget targetYouCognizant
[ performActions
, shootTargetWithStrat targetYouCognizant suppress
, basicPerceptionUpdate [0]
]
, _crInv = IM.fromList [(0,miniGun)]
@@ -105,20 +107,6 @@ miniGunCrit' = defaultCreature
, _crRad = 10
, _crState = defaultState
, _crHP = 500
, _crRationality = ActionRat
{ _crImpulse = []
, _crAction = [AimAtCloseSlow 0 (0,0) 0.2 0.01 (pi/4) ]
}
}
miniGunCrit :: Creature
miniGunCrit = defaultCreature
{ _crPict = basicCrPict red
, _crUpdate = stateUpdate miniAI
, _crInv = IM.fromList [(0,miniGun)]
, _crInvSel = 0
, _crRad = 10
, _crState = defaultState {_goals = [[InitGuard]]}
, _crHP = 500
}
longCrit :: Creature
longCrit = defaultCreature
@@ -133,12 +121,31 @@ longCrit = defaultCreature
multGunCrit :: Creature
multGunCrit = defaultCreature
{ _crPict = basicCrPict red
, _crUpdate = stateUpdate (twitchMissAI 300 350)
-- , _crUpdate = stateUpdate (twitchMissAI 300 350)
, _crInv = IM.fromList [(0,multGun),(1,medkit 100)]
, _crInvSel = 0
, _crRad = 10
, _crState = defaultState {_goals = [[InitGuard]]}
, _crHP = 300
, _crUpdate = stateUpdate $ impulsiveAI $ composeInternalAIs
[ performActions
, watchUpdateStrat
[ (crHasTargetLOS, shootFirstMiss 0)
, (crAwayFromPost, goToPostStrat)
]
, basicPerceptionUpdate [0]
, doStrategyActions
, reloadOverride
, targetYouWhenCognizant
, overrideInternal (onBoth (&&) crHasTarget (crStratConMatches (GetTo (0,0))))
$ \ _ -> crActionPlan . crStrategy .~ WatchAndWait
]
, _crActionPlan = ActionPlan
{ _crImpulse = []
, _crAction = []
, _crStrategy = WatchAndWait
, _crGoal = []
}
}
launcherCrit :: Creature
launcherCrit = defaultCreature
@@ -173,7 +180,25 @@ pistolCrit = defaultCreature
autoCrit :: Creature
autoCrit = defaultCreature
{ _crPict = basicCrPict red
, _crUpdate = stateUpdate basicShooterAI
, _crUpdate = stateUpdate $ impulsiveAI $ composeInternalAIs
[ performActions
, watchUpdateStrat
[ (crHasTargetLOS, shootAdvanceStrat 0)
, (crAwayFromPost, goToPostStrat)
]
, basicPerceptionUpdate [0]
, doStrategyActions
, reloadOverride
, targetYouWhenCognizant
, overrideInternal (onBoth (&&) crHasTarget (crStratConMatches (GetTo (0,0))))
$ \ _ -> crActionPlan . crStrategy .~ WatchAndWait
]
, _crActionPlan = ActionPlan
{ _crImpulse = []
, _crAction = []
, _crStrategy = StrategyActions [StartSentinelPost] WatchAndWait
, _crGoal = []
}
, _crInv = IM.fromList [(0,autoGun),(1,medkit 100)]
, _crInvSel = 0
, _crRad = 10
@@ -246,6 +271,7 @@ startCr = defaultCreature
, _crMaxHP = 1500
, _crInv = startInventory
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ pictures [color (greyN 0.8) $ circleSolid 10, circLine 10]
, _crFaction = PlayerFaction
}
{- |
Items you start with.
@@ -253,6 +279,7 @@ Items you start with.
startInventory = IM.fromList (zip [0..20]
(
[bezierGun
,multGun
,hvAutoGun
,remoteLauncher
,grenade
+8
View File
@@ -355,6 +355,14 @@ advanceStepCounter speed cr = over (crStance . carriage) f cr
creatureTurn :: Float -> Creature -> Creature
creatureTurn a = crDir +~ a
creatureTurnTo :: Point2 -> Creature -> Creature
creatureTurnTo p cr
| vToTarg == (0,0) = cr -- this should deal with the angleVV error
| otherwise = cr & crDir .~ dirToTarget
where
vToTarg = p -.- _crPos cr
dirToTarget = argV vToTarg
creatureTurnToward :: Point2 -> Float -> Creature -> Creature
creatureTurnToward p turnSpeed cr
| vToTarg == (0,0) = cr -- this should deal with the angleVV error
+2 -1
View File
@@ -13,7 +13,8 @@ useItem :: Int -> World -> World
useItem n w = equippedItemEffect n w
crUseItem :: Creature -> World -> World
crUseItem cr = itemEffect (_crID cr) (_crInv cr IM.! _crInvSel cr)
--crUseItem cr = itemEffect (_crID cr) (_crInv cr IM.! _crInvSel cr)
crUseItem cr = tryUseItem (_crID cr)
tryUseItem
:: Int -- ^ Creature id
+194 -7
View File
@@ -1,25 +1,212 @@
module Dodge.Creature.ActionRat
( shootTargetWithStrat
, suppressShootTarget
, suppress
, shootAdvance
, shootFirstMiss
, doStrategyActions
, reloadOverride
, shootAdvanceStrat
, watchUpdateStrat
, goToPostStrat
, overrideInternal
)
where
import Dodge.Data
import Dodge.Base
import Dodge.Base.Collide
import Dodge.Creature.ChooseTarget
import Dodge.Creature.Rationality.Data
import Dodge.Creature.Stance.Data
import Dodge.Creature.Test
import Geometry
import Data.List
import qualified Data.IntMap.Strict as IM
import Control.Lens
shootTargetWithStrat
:: (Creature -> World -> Maybe Creature) -- ^ Function for determining target
-> (World -> Creature -> Creature -> [Action] -> [Action])
-- ^ Function for determining shooting strategy given target
-> World
-> Creature
-> Creature
shootTargetWithStrat targFunc strat w cr = case targFunc cr w of
Nothing -> cr
Just crTarg -> cr & crActionPlan . crAction %~ strat w cr crTarg
{- | Action update for a simple shooting creature -}
shootAtTarget
suppressShootTarget
:: (Creature -> World -> Maybe Creature) -- ^ Function for determining target
-> World
-> Creature
-> Creature
shootAtTarget targFunc w cr = case targFunc cr w of
suppressShootTarget targFunc w cr = case targFunc cr w of
Nothing -> cr
Just crTarg -> cr & crRationality . crAction %~ shootAtTarg w cr crTarg
Just crTarg -> cr & crActionPlan . crAction %~ suppress w cr crTarg
shootAtTarg :: World -> Creature -> Creature -> [Action] -> [Action]
shootAtTarg w cr tcr as
| canSee (_crID cr) (_crID tcr) w = nub (ShootTillEmpty : as)
| otherwise = as
suppress :: World -> Creature -> Creature -> [Action] -> [Action]
suppress w cr tcr as
| canSee (_crID cr) (_crID tcr) w && cr ^. crStance . posture /= Aiming
&& cr ^? crInv . ix (_crInvSel cr) . wpReloadState == Just 0 =
[ DrawWeapon
, WaitThen 50 ShootTillEmpty
, AimAtCloseSlow
{ _targetID = _crID tcr
, _targetSeenAt = _crPos cr
, _aimSpeed = 0.2
, _slowAimSpeed = 0.01
, _slowAimAngle = pi/4
}
]
| otherwise = as
shootFirstMiss :: Int -> World -> Creature -> Strategy
shootFirstMiss tcid w cr = StrategyActions acs (ShootAt tcid)
where
acs =
[ DoActionIfElse NoAction crIsAiming (DoActionThen DrawWeapon (WaitThen 50 NoAction))
`DoActionThen`
LeadTarget (10,50)
`DoActionThen`
advanceShoot
`DoActionThen`
LeadTarget (50,0)
`DoActionThen`
(advanceShoot `DoActionWhileThen` lostest
$ DoReplicateThen advanceShoot 75
$ DoImpulses [ChangeStrategy WatchAndWait]
)
]
lostest (w,cr') = canSee (_crID cr') tcid w
advanceShoot = ImpulsesList [[UseItem, MoveForward 3]]
shootAdvanceStrat :: Int -> World -> Creature -> Strategy
shootAdvanceStrat tcid _ _ = StrategyActions acs (ShootAt tcid)
where
acs =
[ DoActionIfElse NoAction crIsAiming (DoActionThen DrawWeapon (WaitThen 50 NoAction))
`DoActionThen`
(DoActionWhileThen advanceShoot lostest
. DoReplicateThen advanceShoot 75
$ DoImpulses [ChangeStrategy WatchAndWait]
)
, AimAtCloseSlow
{ _targetID = tcid
, _targetSeenAt = (0,0) -- hack
, _aimSpeed = 0.2
, _slowAimSpeed = 0.01
, _slowAimAngle = pi/8
}
]
lostest (w,cr') = canSee (_crID cr') tcid w
advanceShoot = ImpulsesList [[UseItem, MoveForward 3]]
goToPostStrat :: World -> Creature -> Strategy
goToPostStrat w cr = case find sentinelGoal $ _crGoal $ _crActionPlan cr of
Just (SentinelAt p _) -> StrategyActions
[DoActionThen (WaitThen 150 holsterIfAiming)
$ DoActionThen (PathTo p)
NoAction
-- $ DoImpulses [ChangeStrategy WatchAndWait]
] $ GetTo p
_ -> WatchAndWait
where
sentinelGoal (SentinelAt p dir) = True
sentinelGoal _ = False
holsterIfAiming
| crIsAiming (w,cr) = HolsterWeapon
| otherwise = NoAction
overrideInternal
:: ((World , Creature) -> Bool)
-> (World -> Creature -> Creature)
-> World
-> Creature
-> Creature
overrideInternal test update w cr
| test (w,cr) = update w cr
| otherwise = cr
shootAdvance :: World -> Creature -> Creature -> [Action] -> [Action]
shootAdvance w cr tcr as
| lostest (w,cr) && cr ^. crStance . posture /= Aiming
&& cr ^? crInv . ix (_crInvSel cr) . wpReloadState == Just 0 =
[ DrawWeapon
, DoImpulses [ChangeStrategy $ ShootAt $ _crID tcr]
, WaitThen 50
. DoActionWhileThen advanceShoot lostest
. DoReplicateThen advanceShoot 50
$ DoImpulses [ChangeStrategy WatchAndWait]
, AimAtCloseSlow
{ _targetID = _crID tcr
, _targetSeenAt = _crPos cr
, _aimSpeed = 0.2
, _slowAimSpeed = 0.01
, _slowAimAngle = pi/8
}
]
| cr ^. crStance . posture == Aiming && cr ^? crInv . ix (_crInvSel cr) . wpReloadState /= Just 0
= [ HolsterWeapon ]
| otherwise = as
where
lostest (w,cr') = canSee (_crID cr) (_crID tcr) w
advanceShoot = ImpulsesList [[UseItem, MoveForward 3]]
applyNewStrategies
:: World
-> Creature
-> Creature
applyNewStrategies w cr = case cr ^? crActionPlan . crStrategy of
Just (StrategyActions acs strat) -> cr & crActionPlan . crAction .~ acs
& crActionPlan . crStrategy .~ strat
otherwise -> cr
doStrategyActions
:: World
-> Creature
-> Creature
doStrategyActions w cr = case cr ^? crActionPlan . crStrategy of
Just (StrategyActions acs strat) -> cr & crActionPlan . crAction .~ acs
& crActionPlan . crStrategy .~ strat
otherwise -> cr
updateShoot
:: World
-> Creature
-> Creature
updateShoot w cr = case _crStrategy $ _crActionPlan cr of
WatchAndWait -> undefined
reloadOverride
:: World
-> Creature
-> Creature
reloadOverride _ cr
| cr ^? crInv . ix (_crInvSel cr) . wpLoadedAmmo == Just 0
&& cr ^. crStance . posture == Aiming
= cr & crActionPlan . crStrategy .~ StrategyActions reloadActions Reload
| otherwise = cr
where
reloadActions =
[ HolsterWeapon
, WaitThen 1 $ DoActionWhileThen NoAction crIsReloading (DoImpulses [ChangeStrategy WatchAndWait])
]
watchUpdateStrat
:: [ ((World, Creature) -> Bool, World -> Creature -> Strategy) ]
-> World
-> Creature
-> Creature
watchUpdateStrat fs w cr = case cr ^? crActionPlan . crStrategy of
Just WatchAndWait -> cr
& crActionPlan . crStrategy .~ listGuard (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
+2 -2
View File
@@ -14,7 +14,7 @@ chaseTarget
-> Creature
chaseTarget targFunc w cr = case targFunc cr w of
Nothing -> cr
Just crTarg -> cr & crRationality . crImpulse .~ chaseTarg cr crTarg
Just crTarg -> cr & crActionPlan . crImpulse .~ chaseTarg cr crTarg
chaseTarg :: Creature -> Creature -> [Impulse]
chaseTarg cr crT
@@ -40,7 +40,7 @@ impulseShootAtTarget
-> Creature
impulseShootAtTarget targFunc w cr = case targFunc cr w of
Nothing -> cr
Just crTarg -> cr & crRationality . crImpulse .~ impulseShootAtTarg cr crTarg
Just crTarg -> cr & crActionPlan . crImpulse .~ impulseShootAtTarg cr crTarg
impulseShootAtTarg :: Creature -> Creature -> [Impulse]
impulseShootAtTarg cr crT
+1 -3
View File
@@ -20,7 +20,7 @@ import Picture
import qualified Data.IntMap.Strict as IM
import Control.Lens
defaultInanimate = defaultCreature & crState . crIsAnimate .~ False
defaultInanimate = defaultCreature & crActionPlan .~ Inanimate
lamp :: Creature
lamp = defaultInanimate
@@ -60,7 +60,6 @@ barrel = defaultInanimate
]
, _crState = defaultState
{_goals = [[Wait]]
,_faction = ChaseCritters
,_crSpState = Barrel []
}
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
@@ -76,7 +75,6 @@ explosiveBarrel = defaultInanimate
]
, _crState = defaultState
{_goals = [[Wait]]
,_faction = ChaseCritters
,_crSpState = Barrel []
}
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
+3 -2
View File
@@ -6,6 +6,7 @@ module Dodge.Creature.Picture
, circLine
) where
import Dodge.Data
import Dodge.Creature.Stance.Data
import Dodge.Creature.State.Data
import Dodge.Creature.AlertLevel.Data
import Dodge.Picture.Layer
@@ -21,7 +22,7 @@ basicCrPict
-> Picture
basicCrPict col cr = pictures
[ onLayer CrLayer . piercingMod $ bluntScale naked
, drawAwakeLevel cr
-- , drawAwakeLevel cr
, drawEquipment cr
]
where
@@ -66,7 +67,7 @@ drawEquipment
drawEquipment cr = pictures $ map f $ IM.toList (_crInv cr)
where
f (i,it) = case it ^? itEquipPict of
Just g -> g cr i
Just g | (cr ^? crStance . posture) == Just Aiming -> g cr i
_ -> blank
circLine x = line [(0,0),(x,0)]
+51 -10
View File
@@ -6,6 +6,7 @@ import Dodge.Creature.Rationality.Data
import Dodge.Creature.Action
import Dodge.Creature.Action.UseItem
import Dodge.Creature.State.Data
import Dodge.Creature.Stance.Data
import Dodge.SoundLogic
import Geometry
@@ -14,6 +15,8 @@ import qualified Data.IntMap.Strict as IM
import System.Random
import Control.Lens
-- Alternatives would probably be a very good fit for actions...
composeInternalAIs
:: [World -> Creature -> Creature]
-> World
@@ -39,7 +42,7 @@ followImpulses w (f,g) cr
$ foldr
(\imp (f' , cr') -> let (f'', cr'') = followImpulse imp w cr' in (f'' . f', cr''))
(f, cr)
(_crImpulse $ _crRationality cr)
(_crImpulse $ _crActionPlan cr)
where
g' = snd $ next g
@@ -53,14 +56,20 @@ followImpulse imp w cr = case imp of
MoveForward x -> (id, crMvForward x cr)
Turn a -> (id, creatureTurn a cr)
TurnToward p a -> (id, creatureTurnToward p a cr)
-- UseItem -> (crUseItem cr, cr)
TurnTo p -> (id, creatureTurnTo p cr)
ChangePosture post -> (id, cr & crStance . posture .~ post)
UseItem -> (crUseItem cr, cr)
SwitchToItem i -> (id, cr & crInvSel .~ i)
Melee crID ->
(hitCr crID
, crMvBy (10 *.* normalizeV (posFromID crID -.- cpos)) $ cr & crMeleeCooldown ?~ 20) -- randomise cooldown?
RandomTurn a -> (id, creatureTurn (rr a) cr)
_ -> (id , cr)
MakeSound sid -> ( soundOnceOrigin sid (CrSound (_crID cr)) (_crPos cr) , cr )
DropItem -> undefined
ChangeStrategy strat -> (id, cr & crActionPlan . crStrategy .~ strat)
AddGoal gl -> (id, cr & crActionPlan . crGoal %~ (gl :) )
ArbitraryCreatureImpulse f -> (id, f w cr)
_ -> (id , cr)
where
cpos = _crPos cr
posFromID cid = _crPos $ _creatures w IM.! cid
@@ -78,10 +87,10 @@ actionUpdateAI actF w c = performActions w $ actF w c
performActions :: World -> Creature -> Creature
performActions w cr = cr
& crRationality . crImpulse .~ concat iss
& crRationality . crAction .~ catMaybes mayas
& crActionPlan . crImpulse .~ concat iss
& crActionPlan . crAction .~ catMaybes mayas
where
(iss, mayas) = unzip $ map (performAction cr w) $ cr ^. crRationality . crAction
(iss, mayas) = unzip $ map (performAction cr w) $ cr ^. crActionPlan . crAction
{- | Performing an action means that a creature has some impulses for a frame, and
updates or deletes the action itself. -}
@@ -91,9 +100,9 @@ performAction
-> Action
-> ( [Impulse] , Maybe Action )
performAction cr w ac = case ac of
ShootTillEmpty -> case cr ^? crInv . ix (_crInvSel cr) . wpLoadedAmmo of
Just x | x > 0 -> ( [UseItem] , Just ShootTillEmpty )
_ -> ( [] , Nothing )
ShootTillEmpty -> case cr ^? crInv . ix (_crInvSel cr) . wpReloadState of
Just x | x == 0 -> ( [UseItem] , Just ShootTillEmpty )
_ -> ( [] , Just (WaitThen 20 HolsterWeapon) )
AimAtCloseSlow tcid p speed slowSpeed a
| canSee (_crID cr) tcid w && safeAngleVV (unitVectorAtAngle cdir) (tpos -.- cpos) < a
-> ([TurnToward tpos slowSpeed] , Just $ AimAtCloseSlow tcid tpos speed slowSpeed a)
@@ -104,9 +113,41 @@ performAction cr w ac = case ac of
| otherwise -> ([TurnToward p speed] , Just $ AimAtCloseSlow tcid p speed slowSpeed a)
where
tpos = _crPos (_creatures w IM.! tcid)
WaitThen 0 newAc -> ([] , Just newAc)
WaitThen t newAc -> ([] , Just (WaitThen (t-1) newAc))
ImpulsesList (xs:xss) -> (xs, Just $ ImpulsesList xss)
ImpulsesList _ -> ([], Nothing)
DoImpulses imps -> (imps, Nothing)
DrawWeapon -> ([ChangePosture Aiming, MakeSound pickUpSound] , Nothing)
HolsterWeapon -> ([ChangePosture AtEase, MakeSound putDownSound] , Nothing)
DoActionThen ac ac' -> case performAction cr w ac of
(imps , Just ac'') -> (imps, Just (DoActionThen ac'' ac'))
(imps , Nothing ) -> (imps, Just ac')
DoActionWhile ac f
| f (w,cr) -> (fst $ performAction cr w ac, Just $ DoActionWhile ac f)
| otherwise -> ([], Nothing)
DoActionIfElse ac f ac'
| f (w,cr) -> performAction cr w ac
| otherwise -> performAction cr w ac'
DoActionWhileThen ac f ac'
| f (w,cr) -> (fst $ performAction cr w ac, Just $ DoActionWhileThen ac f ac')
| otherwise -> performAction cr w ac'
DoReplicateThen ac 0 ac' -> performAction cr w ac'
DoReplicateThen ac t ac' -> (fst $ performAction cr w ac, Just $ DoReplicateThen ac (t-1) ac')
DoActions [] -> ([], Nothing)
DoActions acs ->
let (imps, newAcs) = unzip $ map (performAction cr w) acs
in (concat imps, Just . DoActions $ catMaybes newAcs)
StartSentinelPost -> ([AddGoal $ SentinelAt (_crPos cr) (_crDir cr)], Nothing)
PathTo p
| dist cpos p < 5 -> ([], Nothing)
| hasLOS cpos p w -> ([TurnToward p (pi/4),MoveForward 3] , Just (PathTo p))
| otherwise -> ([], Nothing)
LeadTarget p -> case cr ^? crTarget . _Just of
Just tcid -> let tcr = _creatures w IM.! tcid
in ([TurnTo (_crPos tcr +.+ rotateV (_crDir tcr) p)], Nothing)
_ -> ([], Nothing)
_ -> ([], Nothing)
where
cpos = _crPos cr
cdir = _crDir cr
-61
View File
@@ -6,64 +6,3 @@ import Dodge.Creature.Stance.Data
import Geometry.Data
import Control.Lens
data Rationality
= NonRat
| ImpulseRat
{_crImpulse :: [Impulse] }
| ActionRat
{_crImpulse :: [Impulse]
,_crAction :: [Action]
}
| StrategyRat
{_crImpulse :: [Impulse]
,_crAction :: [Action]
,_crStrategy :: Strategy
}
data Impulse
= Move Point2
| MoveForward Float
| Turn Float
| RandomTurn Float
| TurnToward Point2 Float
| UseItem
| SwitchToItem Int
| DropItem
| PickupNearby Int
| UseWorldObject Int
| Bark -- placeholder for various communication types
| UseIntrinsicAbility
| Melee Int
| ChangePosture Posture
deriving (Eq,Ord,Show)
data Action
= Attack Int
| AimAtCloseSlow
{_targetID :: Int
,_targetSeenAt :: Point2
,_aimSpeed :: Float
,_slowAimSpeed :: Float
,_slowAimAngle :: Float
}
| MeleeAttack Point2 Int
| PathTo Point2
| FleeFrom Int
| HealSelf
| DefendSelf
| Protect Int
| SearchFor Int
| Search
| PickupItem Int
| ShootTillEmpty
| ImpulsesList [[Impulse]]
deriving (Eq,Ord,Show)
data Strategy
= Flank Int
| Ambush Int
| Lure Int Point2
| GuardArea Point2
| Patrol [Point2]
makeLenses ''Rationality
makeLenses ''Impulse
+16
View File
@@ -0,0 +1,16 @@
{- |
Deals with setting a target for creatures
-}
module Dodge.Creature.SetTarget
where
import Dodge.Data
import Dodge.Creature.AlertLevel.Data
import Control.Lens
targetYouWhenCognizant
:: World
-> Creature
-> Creature
targetYouWhenCognizant w cr = case cr ^? crAwarenessLevel . ix 0 of
Just (Cognizant _) -> cr & crTarget .~ Just 0
_ -> cr & crTarget .~ Nothing
+69 -69
View File
@@ -116,23 +116,24 @@ movementSideEff cr w
| otherwise = case cr ^? crStance . carriage of
Just (Walking x y) -> takeStep x y w
_ -> 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
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
(v1:v2:v3:_) = fst $ runState ((sequence . repeat . randInCirc) 0.1) $ _randGen w
(r1:r2:r3:_) = map ((*.*) 100) (v2:v3:v1:[])
(v1:v2:v3:_) = fst $ runState ((sequence . repeat . randInCirc) 0.1) $ _randGen w
(r1:r2:r3:_) = map ((*.*) 100) (v2:v3:v1:[])
crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5
takeStep x y | crHasMoved && x < 20 && x + y >= 20 = soundMultiFrom footor 22 3 0
| crHasMoved && x < 80 && x + y >= 80 = soundMultiFrom footor 23 3 0
| otherwise = id
footor = [FootstepSound 0,FootstepSound 1]
crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5
takeStep x y | crHasMoved && x < 20 && x + y >= 20 = soundMultiFrom footor 22 3 0
| crHasMoved && x < 80 && x + y >= 80 = soundMultiFrom footor 23 3 0
| otherwise = id
footor = [FootstepSound 0,FootstepSound 1]
invSideEff :: Creature -> World -> World
invSideEff cr w = weaponReloadSounds cr $ IM.foldrWithKey f w (_crInv cr)
@@ -142,29 +143,22 @@ invSideEff cr w = weaponReloadSounds cr $ IM.foldrWithKey f w (_crInv cr)
weaponReloadSounds :: Creature -> World -> World
weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of
Just (Weapon {_wpReloadState = 0})
-> w
Just (Weapon {_wpReloadState = 1})
-> stopSoundFrom (CrReloadSound cid) w
Just (Weapon {})
-> soundFrom (CrReloadSound cid) reloadSound (1) 0 w
_
-> w
where cid = _crID cr
Just (Weapon {_wpReloadState = 0}) -> w
Just (Weapon {_wpReloadState = 1}) -> stopSoundFrom (CrReloadSound cid) w
Just (Weapon {}) -> soundFrom (CrReloadSound cid) reloadSound (1) 0 w
_ -> w
where
cid = _crID cr
updateMovement :: StdGen -> Creature -> (Creature, StdGen)
updateMovement g cr
| isFrictionless cr
= (over crPos (+.+ momentum) $ setOldPos cr, g')
| otherwise
= case cr ^? crStance . carriage of
Just (Walking x y)
-> (set (crStance . carriage) (Walking ((x+y)`mod`120) 0)
$ setOldPos
cr, g)
_ -> (set (crStance . carriage) (Walking 0 0)
$ setOldPos
cr, g)
| isFrictionless cr = (over crPos (+.+ momentum) $ setOldPos cr, g')
| otherwise = case cr ^? crStance . carriage of
Just (Walking x y)
-> (set (crStance . carriage) (Walking ((x+y)`mod`120) 0) $ setOldPos cr
, g)
_ -> (set (crStance . carriage) (Walking 0 0) $ setOldPos cr
, g)
where
momentum' = 0.98 *.* (_crPos cr -.- _crOldPos cr)
momentum'' | magV momentum' > 1 = 1 *.* normalizeV momentum'
@@ -180,25 +174,30 @@ isFrictionless cr = case cr ^? crStance . carriage of
updateReloadCounter :: Creature -> Creature
updateReloadCounter cr = over (crInv . ix iSel . itUseTime) decreaseToZero
. over (crInv . ix iSel . wpReloadState) decreaseToZero
$ cr
where iSel = _crInvSel cr
. over (crInv . ix iSel . wpReloadState) decreaseToZero
$ cr
where
iSel = _crInvSel cr
decreaseToZero :: Int -> Int
decreaseToZero x | x > 0 = x - 1
| otherwise = 0
decreaseToZero x = max 0 (x - 1)
onDeath :: (Creature -> World -> World) -> CRUpdate -> CRUpdate
onDeath h u w (f,g) cr
| _crHP cr > 0 = u w (f,g) cr
| otherwise = ( ( h cr . f, g ) , Nothing )
updateBarrel ::
World -> (World -> World,StdGen) -> Creature -> ((World -> World , StdGen), Maybe Creature)
updateBarrel w (f,g) cr | _crHP cr > 0 = ((f, g), newCr)
| otherwise = ((f, g), Nothing)
where damages = _crDamage $ _crState cr
newCr = Just $ doDamage cr
updateBarrel
:: World
-> (World -> World,StdGen)
-> Creature
-> ((World -> World , StdGen), Maybe Creature)
updateBarrel w (f,g) cr
| _crHP cr > 0 = ((f, g), newCr)
| otherwise = ((f, g), Nothing)
where
damages = _crDamage $ _crState cr
newCr = Just $ doDamage cr
-- it is easy to leave off the "f" here
-- should find some better way of doing all this that is less prone to error
@@ -207,29 +206,30 @@ updateExpBarrel ::
updateExpBarrel w (f,g) cr
| _crHP cr > 0 = ((f . foldr (.) id pierceSparks . hiss, g'), newCr)
| otherwise = ((f . makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing)
where damages = _crDamage $ _crState cr
pierceSparks :: [World -> World]
pierceSparks
= zipWith4 (\p a colid time-> (createBarrelSpark time colid (_crPos cr +.+ p) (a + argV p))
(Just $ _crID cr))
poss as colids times
as = randomRs (-0.7,0.7) $ g
colids = randomRs (0,11) $ g
times = randomRs (2,5) $ g
(g',_) = split g
poss = _piercedPoints $ _crSpState $ _crState cr
--newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages
newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr
perforate :: DamageType -> Creature -> Creature
perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints)
((:) $ int -.- _crPos cr) cr
perforate _ cr = cr
applyFuseDamage cr = over crHP (\hp -> hp - length (_piercedPoints
$ _crSpState $ _crState cr))
cr
hiss | poss == [] = id
| otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1)
where
damages = _crDamage $ _crState cr
pierceSparks :: [World -> World]
pierceSparks
= zipWith4 (\p a colid time-> (createBarrelSpark time colid (_crPos cr +.+ p) (a + argV p))
(Just $ _crID cr))
poss as colids times
as = randomRs (-0.7,0.7) $ g
colids = randomRs (0,11) $ g
times = randomRs (2,5) $ g
(g',_) = split g
poss = _piercedPoints $ _crSpState $ _crState cr
--newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages
newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr
perforate :: DamageType -> Creature -> Creature
perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints)
((:) $ int -.- _crPos cr) cr
perforate _ cr = cr
applyFuseDamage cr = over crHP (\hp -> hp - length (_piercedPoints
$ _crSpState $ _crState cr))
cr
hiss | poss == [] = id
| otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1)
damToExpBarrel :: [DamageType] -> Creature -> Creature
damToExpBarrel ds cr = foldr damToExpBarrel' (foldr damToExpBarrel' cr pierceDam) otherDam
+3 -2
View File
@@ -6,17 +6,16 @@ import Geometry
import Dodge.Data.DamageType
import Dodge.Creature.Impulse.Data
import Dodge.Creature.Stance.Data
import Picture.Data
import Control.Lens
data CreatureState = CrSt
{ _goals :: [[Impulse]]
, _faction :: Faction
, _crDamage :: [DamageType]
, _crPastDamage :: [[DamageType]]
, _crSpState :: CrSpState
, _crDropsOnDeath :: CreatureDropType
, _crIsAnimate :: Bool
}
data CreatureDropType
@@ -36,6 +35,8 @@ data Faction
| ChaseCritters
| SpawnedBy Int
| NoFaction
| ColorFaction Color
| PlayerFaction
deriving (Eq,Show)
makeLenses ''CreatureState
+54
View File
@@ -0,0 +1,54 @@
{- |
Module testing for properties of creatures, whether they are reloading, etc.
Each function takes the world and a creature and returns a bool.
Note that the creature NEED NOT be the same as the creature with that id in the world,
in fact in some cases a creature with that id may not even exist.
-}
module Dodge.Creature.Test
where
import Dodge.Data
import Dodge.Base.Collide
import Dodge.Creature.Stance.Data
import Geometry
import SameConstr
import Data.List (find)
import Data.Maybe
import qualified Data.IntMap.Strict as IM
import Control.Lens
andTest :: (a -> Bool) -> (a -> Bool) -> a -> Bool
andTest f g a = f a && g a
onBoth :: (a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c
onBoth f g h x = f (g x) (h x)
crIsReloading :: (World, Creature) -> Bool
crIsReloading (w,cr) = case cr ^? crInv . ix (_crInvSel cr) . wpReloadState of
Just t -> t > 0
_ -> False
crCanSeeCID :: Int -> (World, Creature) -> Bool
crCanSeeCID cid (w,cr) = hasLOS (_crPos cr) (_crPos $ _creatures w IM.! cid) w
crIsAiming :: (World,Creature) -> Bool
crIsAiming (_,cr) = _posture (_crStance cr) == Aiming
crHasTarget :: (World,Creature) -> Bool
crHasTarget (_,cr) = isJust $ cr ^? crTarget . _Just
crHasTargetLOS :: (World,Creature) -> Bool
crHasTargetLOS (w,cr) = case cr ^? crTarget . _Just of
Just i -> crCanSeeCID i (w,cr)
Nothing -> False
crStratConMatches :: Strategy -> (World,Creature) -> Bool
crStratConMatches strat (_,cr) = eqConstr strat (_crStrategy $ _crActionPlan cr)
crAwayFromPost :: (World,Creature) -> Bool
crAwayFromPost (_,cr) = case find sentinelGoal $ _crGoal $ _crActionPlan cr of
Just (SentinelAt p dir) -> dist p (_crPos cr) > 15
_ -> False
where
sentinelGoal (SentinelAt p dir) = True
sentinelGoal _ = False
+141 -1
View File
@@ -6,6 +6,7 @@ circular imports are probably not a good idea.
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE DeriveGeneric #-}
module Dodge.Data
( module Dodge.Data
, module Dodge.Data.DamageType
@@ -33,9 +34,11 @@ import Geometry.Data
import Sound.Data
import qualified DoubleStack as DS
import GHC.Generics
import Control.Lens
import Control.Monad.State
import System.Random
import Data.Data
import Data.Graph.Inductive
import Data.Int (Int16)
import qualified Data.Set as S
@@ -151,11 +154,13 @@ data Creature = Creature
, _crCorpse :: Picture
, _crApplyDamage :: [DamageType] -> Creature -> (World -> World,Creature)
, _crStance :: Stance
, _crRationality :: Rationality
, _crActionPlan :: ActionPlan
, _crMeleeCooldown :: Maybe Int
, _crAwakeLevel :: AwakeLevel
, _crAttentionDir :: AttentionDir
, _crAwarenessLevel :: IM.IntMap AwarenessLevel
, _crFaction :: Faction
, _crTarget :: Maybe Int
}
data WorldState
= DoorNumOpen Int
@@ -443,6 +448,138 @@ data ForceField = FF
}
data FFState = FFDestroyable { _ffsHP :: Int }
data ActionPlan
= Inanimate
| ActionPlan
{_crImpulse :: [Impulse]
,_crAction :: [Action]
,_crStrategy :: Strategy
,_crGoal :: [Goal]
}
data Impulse
= Move Point2
| MoveForward Float
| StepForward
| Turn Float
| RandomTurn Float
| TurnToward Point2 Float
| TurnTo Point2
| UseItem
| SwitchToItem Int
| DropItem
| PickupNearby Int
| UseWorldObject Int
| Bark -- placeholder for various communication types
| UseIntrinsicAbility
| Melee Int
| ChangePosture Posture
| MakeSound Int
| ChangeStrategy Strategy
| AddGoal Goal
| ArbitraryCreatureImpulse (World -> Creature -> Creature)
-- deriving (Eq,Ord,Show)
data Action
= Attack
{_attackTargetID :: Int}
| AimAtCloseSlow
{_targetID :: Int
,_targetSeenAt :: Point2
,_aimSpeed :: Float
,_slowAimSpeed :: Float
,_slowAimAngle :: Float
}
| MeleeAttack
{_meleeAttackLastSeen :: Point2
,_meleeAttackTargetID :: Int
}
| PathTo
{_pathToPoint :: Point2
}
| FleeFrom
{_fleeFromCID :: Int
}
| HealSelf
| DefendSelf
| Protect
{_protectCID :: Int
}
| SearchFor
{_searchForCID :: Int
}
| Search
| PickupItem
{_pickupItemID :: Int
}
| ShootTillEmpty
| ImpulsesList
{_impulsesListList :: [[Impulse]]
}
| DoImpulses
{_doImpulsesList :: [Impulse]
}
| DrawWeapon
| HolsterWeapon
| WaitThen
{_waitThenTimer :: Int
,_waitThenAction :: Action
}
| DoActionWhile
{_doActionWhileAction :: Action
,_doActionWhileCondition :: ( (World, Creature) -> Bool)
}
| DoActionIfElse
{_doActionIfElseIfAction :: Action
,_doActionIfElseCondition :: ( (World, Creature) -> Bool)
,_doActionIfElseElseAction :: Action
}
| DoActionWhileThen
{_doActionWhileThenDo :: Action
,_doActionWhileThenCondition :: ( (World, Creature) -> Bool)
,_doActionWhileThenThen :: Action
}
| DoActions
{_doActionsList :: [Action]
}
| DoActionOnce
{_doActionOnceAction :: Action
}
| DoActionThen
{_doActionThenFirst :: Action
,_doActionThenSecond :: Action
}
| DoGuardActions
{_doGuardActionsList :: [( (World, Creature) -> Bool, Action, Maybe Action)]
}
| DoReplicateThen
{_doReplicateThenDo :: Action
,_doReplicateThenTimes :: Int
,_doReplicateThenAfter :: Action
}
| LeadTarget
{_leadTargetBy :: Point2
}
| NoAction
| StartSentinelPost
deriving (Generic)
-- deriving (Eq,Ord,Show)
data Strategy
= Flank Int
| Ambush Int
| Lure Int Point2
| Patrol [Point2]
| ShootAt Int
| FollowImpulses
| WatchAndWait
| StrategyActions [Action] Strategy
| GetTo Point2
| Reload
deriving (Generic)
-- deriving (Eq,Ord,Show)
data Goal
= LiveLongAndProsper
| Kill Int
| SentinelAt Point2 Float
makeLenses ''World
makeLenses ''Cloud
makeLenses ''Creature
@@ -460,6 +597,9 @@ makeLenses ''ForceField
makeLenses ''FFState
makeLenses ''PressPlate
makeLenses ''Button
makeLenses ''ActionPlan
makeLenses ''Impulse
makeLenses ''Action
numColor :: Int -> Color
numColor 0 = (1,0,0,1)
+3 -3
View File
@@ -91,20 +91,20 @@ defaultCreature = Creature
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 10
, _crApplyDamage = defaultApplyDamage
, _crStance = Stance {_carriage=Walking 0 0,_posture=AtEase}
, _crRationality = ImpulseRat []
, _crActionPlan = ActionPlan [] [] WatchAndWait [LiveLongAndProsper]
, _crMeleeCooldown = Nothing
, _crAwakeLevel = Vigilant
, _crAttentionDir = AttentiveTo []
, _crAwarenessLevel = IM.empty
, _crFaction = NoFaction
, _crTarget = Nothing
}
defaultState = CrSt
{ _goals = []
, _faction = NoFaction
, _crDamage = []
, _crPastDamage = []
, _crSpState = GenCr
, _crDropsOnDeath = DropAmount 1
, _crIsAnimate = True
}
defaultEquipment = Equipment
{ _itIdentity = Generic
+1 -1
View File
@@ -50,7 +50,7 @@ roomTreex = do
[[StartRoom]
,[Corridor]
,[SpecificRoom . pure . pure . Right $ roomGlassOctogon 400
& rmPS %~ (PS (0,0) 0 (PutCrit miniGunCrit') :)
& rmPS %~ (PS (0,-300) 0 (PutCrit multGunCrit) :)
]
,[Corridor]
,[Corridor]
+4 -5
View File
@@ -36,11 +36,10 @@ charFiringStrat
-> Int -- ^ Creature id
-> World
-> World
charFiringStrat strats cid w =
let Just (c :<| _) = w ^? creatures . ix cid . crInv
. ix (_crInvSel $ _creatures w IM.! cid) . itAttachment
. _Just . itCharMode
in fromJust (Prelude.lookup c strats) cid w
charFiringStrat strats cid w = case w ^? creatures . ix cid . crInv
. ix (_crInvSel $ _creatures w IM.! cid) . itAttachment . _Just . itCharMode of
Just (c :<| _) -> fromJust (Prelude.lookup c strats) cid w
Nothing -> w
increaseFuse
:: Int -- ^ Old fuse time
+1 -1
View File
@@ -96,7 +96,7 @@ autoDoorPane (trigx,trigy) n closedPos openPos = Door
a = fst closedPos
b = snd closedPos
dm w
| any (crNearSeg 40 trigx trigy) $ IM.filter (_crIsAnimate . _crState) $ _creatures w
| any (crNearSeg 40 trigx trigy) $ IM.filter isAnimate $ _creatures w
= flip (foldr changeZonedWall) zoneps $ over walls (IM.adjust openDoor n) w
| otherwise
= flip (foldr changeZonedWall') zoneps $ over walls (IM.adjust closeDoor n) w
+2 -23
View File
@@ -42,8 +42,10 @@ module Dodge.SoundLogic (
, smokeTrailSound
, foot1Sound
, foot2Sound
, module Dodge.SoundLogic.Synonyms
) where
import Dodge.Data
import Dodge.SoundLogic.Synonyms
import Sound.Data (SoundStatus (..))
import Geometry.Vector
import Geometry (dist)
@@ -180,26 +182,3 @@ soundMultiFrom (so:sos) sType time fadeTime w
stopSoundFrom :: SoundOrigin -> World -> World
stopSoundFrom so = over (sounds . ix so . soundTime) (fmap $ min 0)
reloadSound,putDownSound,pickUpSound,fireSound,grenadeBang,healSound,teleSound,twoStepSlowSound :: Int
clickSound = 1
reloadSound = 2
pickUpSound = 4
putDownSound = 5
fireSound = 6
grenadeBang = 7
tapQuiet = 8
twoStepSound = 9
healSound = 10
doorSound = 11
twoStepSlowSound = 12
knifeSound = 13
buzzSound = 14
hitSound = 15
autoGunSound = 16
shotgunSound = 17
teleSound = 18
longGunSound = 19
launcherSound = 20
smokeTrailSound = 21
foot1Sound = 22
foot2Sound = 23
+29
View File
@@ -0,0 +1,29 @@
module Dodge.SoundLogic.Synonyms
where
reloadSound,putDownSound,pickUpSound,fireSound,grenadeBang,healSound,teleSound,twoStepSlowSound :: Int
clickSound = 1
reloadSound = 2
pickUpSound = 4
putDownSound = 5
fireSound = 6
grenadeBang = 7
tapQuiet = 8
twoStepSound = 9
healSound = 10
doorSound = 11
twoStepSlowSound = 12
knifeSound = 13
buzzSound = 14
hitSound = 15
autoGunSound = 16
shotgunSound = 17
teleSound = 18
longGunSound = 19
launcherSound = 20
smokeTrailSound = 21
foot1Sound = 22
foot2Sound = 23
drawWeaponSound = 4
holsterWeaponSound = 5
+31 -27
View File
@@ -1,5 +1,7 @@
{- |
Deals with moving creature wall collisions.
-}
module Dodge.WallCreatureCollisions where
-- imports {{{
import Dodge.Data
import Dodge.Creature.State.Data
import Dodge.Base
@@ -43,12 +45,11 @@ wallBuffer = 3
-- out from the wall
-- this is then repeated if the point ends up on a new wall
collideWalls :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2
collideWalls rad cp1 walls cp2
= case (listToMaybe.mapMaybe (pushOutFromWall rad cp2)) walls of
Nothing -> cp2
Just cp3 -> case (listToMaybe.reverse.mapMaybe (pushOutFromWall rad cp3)) walls of
Nothing -> cp3
Just cp4 -> cp4
collideWalls rad cp1 walls cp2 = case (listToMaybe.mapMaybe (pushOutFromWall rad cp2)) walls of
Nothing -> cp2
Just cp3 -> case (listToMaybe.reverse.mapMaybe (pushOutFromWall rad cp3)) walls of
Nothing -> cp3
Just cp4 -> cp1
-- pushes a point out from a list of walls
-- if multiple new points occur, chooses the one closest to the orignal point
@@ -72,15 +73,16 @@ pushOrCrush wls cr = case mapMaybe (pushOutFromWall (_crRad cr) cpos) wls of
-- assumes wall points are different
pushOutFromWall :: Float -> Point2 -> (Point2,Point2) -> Maybe (Point2)
pushOutFromWall rad cp2 (wp1,wp2)
| isOnWall = Just newP -- +.+ (1 *.* norm))
| otherwise = Nothing
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp1' = (rad *.* norm) +.+ wp1
wp2' = (rad *.* norm) +.+ wp2
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isOnWall = circOnSegNoEndpoints wp1 wp2 cp2 rad
isJust Nothing = False
isJust _ = True
| isOnWall = Just newP -- +.+ (1 *.* norm))
| otherwise = Nothing
where
norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp1' = (rad *.* norm) +.+ wp1
wp2' = (rad *.* norm) +.+ wp2
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isOnWall = circOnSegNoEndpoints wp1 wp2 cp2 rad
isJust Nothing = False
isJust _ = True
pushOutFromCorners :: World -> Creature -> Creature
pushOutFromCorners w cr = cr & crPos .~ newPos
@@ -93,8 +95,9 @@ collideCorners rad p1 ps p2 = foldr (intersectCirclePoint rad) p2 ps
-- collide circles with points (outer corners)
intersectCirclePoint :: Float -> Point2 -> Point2 -> Point2
intersectCirclePoint rad p cCen | dist cCen p > rad = cCen
| otherwise = p +.+ (rad *.* errorNormalizeV 65 (cCen -.- p))
intersectCirclePoint rad p cCen
| dist cCen p > rad = cCen
| otherwise = p +.+ (rad *.* errorNormalizeV 65 (cCen -.- p))
checkPushThroughs :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2
checkPushThroughs rad cp1 walls cp2
@@ -102,12 +105,13 @@ checkPushThroughs rad cp1 walls cp2
checkPushThrough :: Float -> Point2 -> Point2 -> (Point2,Point2) -> Maybe (Point2)
checkPushThrough rad cp1 cp2 (wp1,wp2)
| isPushedThrough = intersectSegSeg' cp1 cp2 wp1 wp2
| otherwise = Nothing
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp1' = (rad *.* norm) +.+ wp1
wp2' = (rad *.* norm) +.+ wp2
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isPushedThrough = isRHS wp1 wp2 cp2 && isJust (intersectSegSeg' cp1 cp2 wp1 wp2)
isJust Nothing = False
isJust _ = True
| isPushedThrough = intersectSegSeg' cp1 cp2 wp1 wp2
| otherwise = Nothing
where
norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp1' = (rad *.* norm) +.+ wp1
wp2' = (rad *.* norm) +.+ wp2
newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isPushedThrough = isRHS wp1 wp2 cp2 && isJust (intersectSegSeg' cp1 cp2 wp1 wp2)
isJust Nothing = False
isJust _ = True
+1
View File
@@ -268,6 +268,7 @@ nRaysRad n x = take n $ iterate (rotateV (2*pi/fromIntegral n)) (x,0)
-- | Test whether an angle is to the left of another angle, according to the
-- smallest change in rotation between them.
-- This appears to sometimes fail if the angles are not normalized.
isLeftOfA :: Float -> Float -> Bool
isLeftOfA angle1 angle2 =
(angle1 - angle2 < pi && angle1 > angle2)
+37
View File
@@ -0,0 +1,37 @@
{-# LANGUAGE DefaultSignatures, TypeOperators, FlexibleContexts #-}
{- | Pulled from
https://stackoverflow.com/questions/10112733/haskell-simple-constructor-comparison-function
-}
module SameConstr where
import GHC.Generics
import Data.Function (on)
--class EqC a where
-- eqConstr :: a -> a -> Bool
-- default eqConstr :: (Generic a, GEqC (Rep a)) => a -> a -> Bool
-- eqConstr = geqConstr `on` from
eqConstr :: (Generic a, GEqC (Rep a)) => a -> a -> Bool
eqConstr = geqConstr `on` from
class GEqC f where
geqConstr :: f p -> f p -> Bool
{-# INLINE geqConstr #-}
geqConstr _ _ = True
instance GEqC f => GEqC (M1 i c f) where
{-# INLINE geqConstr #-}
geqConstr (M1 x) (M1 y) = geqConstr x y
instance GEqC (K1 i c)
instance GEqC (f :*: g)
instance GEqC U1
instance GEqC V1
instance (GEqC f, GEqC g) => GEqC (f :+: g) where
{-# INLINE geqConstr #-}
geqConstr (L1 x) (L1 y) = geqConstr x y
geqConstr (R1 x) (R1 y) = geqConstr x y
geqConstr _ _ = False