Refactor ai

This commit is contained in:
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 module Dodge.AIs
( basicShooterAI ( twitchMissAI
, twitchMissAI
, launcherAI , launcherAI
, chargeAI , chargeAI
, sniperAI , sniperAI
, dodgeAI , dodgeAI
, miniAI
, spawnerAI , spawnerAI
) where ) where
import Dodge.Creature.Stance.Data import Dodge.Creature.Stance.Data
import Dodge.Creature.State.Data import Dodge.Creature.State.Data
import Dodge.Creature.Impulse.Data import Dodge.Creature.Impulse.Data
import Dodge.Data import Dodge.Data hiding (Action (..), Impulse (..), Strategy (..))
import Dodge.Base import Dodge.Base
import Dodge.Path import Dodge.Path
import Dodge.SoundLogic import Dodge.SoundLogic
@@ -64,7 +62,7 @@ addImpulse newActions = over (crState . goals) (\(as:ass) -> (newActions++as):as
factionIs :: Faction -> Creature -> Bool 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 :: Point2 -> IM.IntMap Creature -> Point2 -> Point2
lineOrth ypos crs cpos = p lineOrth ypos crs cpos = p
@@ -228,7 +226,7 @@ spawnerAI spawn w (f,g) cr =
addImpulse newActions addImpulse newActions
= Just . over (crState . goals) (\(as:ass) -> (newActions++as):ass) = 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) $ IM.elems $ _creatures w)
< 15 < 15
spawnChild = placeCrFaction spawn (SpawnedBy cid) (cpos +.+ rotateV dir (1,0)) cpos (_crDir cr) 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 :: Creature -> Faction -> Point2 -> Point2 -> Float -> World -> World
placeCrFaction cr fact p op rot w = over creatures addCr w placeCrFaction cr fact p op rot w = over creatures addCr w
where addCr crs = IM.insert (Dodge.Base.newKey crs) 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} $ cr {_crPos = p,_crOldPos = op,_crDir = rot,_crID = Dodge.Base.newKey crs}
) )
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 :: Float -> Float -> World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
strafeOrChargeAI inRange outRange w (f,g) cr = strafeOrChargeAI inRange outRange w (f,g) cr =
let cpos = _crPos cr let cpos = _crPos cr
@@ -1316,106 +1208,6 @@ suppressShooterAI w (f,g) cr =
| otherwise | otherwise
-> ( (f,g) , replaceImpulse [AimAt p (i-1)] $ turnCloseSlow p cr) -> ( (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 :: World -> (World -> World,StdGen) -> Creature -> ((World -> World,StdGen), Maybe Creature)
sniperAI w (f,g) cr = sniperAI w (f,g) cr =
+11
View File
@@ -58,6 +58,11 @@ aCrPos i w = _crPos $ _creatures w IM.! i
yourItem :: World -> Item yourItem :: World -> Item
yourItem w = _crInv (you w) IM.! _crInvSel (you w) 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))) yourItemRef w = (creatures . ix (_yourID w) . crInv . ix (_crInvSel (you w)))
wallNormal :: Wall -> Point2 wallNormal :: Wall -> Point2
@@ -495,3 +500,9 @@ mvPointToward
mvPointToward !ep !p mvPointToward !ep !p
| dist p ep < 1 = ep | dist p ep < 1 = ep
| otherwise = p +.+ normalizeV (ep -.- p) | 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.ActionRat
import Dodge.Creature.Rationality.Data import Dodge.Creature.Rationality.Data
import Dodge.Creature.AlertLevel import Dodge.Creature.AlertLevel
import Dodge.Creature.SetTarget
import Dodge.Creature.Test
import Dodge.Data import Dodge.Data
import Dodge.AIs import Dodge.AIs
import Dodge.Default import Dodge.Default
@@ -64,10 +66,10 @@ smallChaseCrit = defaultCreature
, _crPict = basicCrPict green , _crPict = basicCrPict green
, _crState = defaultState , _crState = defaultState
{_goals = [[Wait]] {_goals = [[Wait]]
,_faction = ChaseCritters
} }
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)] , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 4 , _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 4
, _crFaction = ColorFaction green
} }
chaseCrit :: Creature chaseCrit :: Creature
chaseCrit = defaultCreature chaseCrit = defaultCreature
@@ -76,10 +78,10 @@ chaseCrit = defaultCreature
, _crPict = basicCrPict green , _crPict = basicCrPict green
, _crState = defaultState , _crState = defaultState
{_goals = [[Wait]] {_goals = [[Wait]]
,_faction = ChaseCritters
} }
, _crInv = IM.empty , _crInv = IM.empty
, _crMeleeCooldown = Just 0 , _crMeleeCooldown = Just 0
, _crFaction = ColorFaction green
} }
armourChaseCrit :: Creature armourChaseCrit :: Creature
armourChaseCrit = defaultCreature armourChaseCrit = defaultCreature
@@ -92,12 +94,12 @@ armourChaseCrit = defaultCreature
,(1,medkit 200) ,(1,medkit 200)
] ]
} }
miniGunCrit' :: Creature miniGunCrit :: Creature
miniGunCrit' = defaultCreature miniGunCrit = defaultCreature
{ _crPict = basicCrPict red { _crPict = basicCrPict red
, _crUpdate = stateUpdate $ impulsiveAI $ composeInternalAIs , _crUpdate = stateUpdate $ impulsiveAI $ composeInternalAIs
[ actionUpdateAI performActions [ performActions
, shootAtTarget targetYouCognizant , shootTargetWithStrat targetYouCognizant suppress
, basicPerceptionUpdate [0] , basicPerceptionUpdate [0]
] ]
, _crInv = IM.fromList [(0,miniGun)] , _crInv = IM.fromList [(0,miniGun)]
@@ -105,20 +107,6 @@ miniGunCrit' = defaultCreature
, _crRad = 10 , _crRad = 10
, _crState = defaultState , _crState = defaultState
, _crHP = 500 , _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 :: Creature
longCrit = defaultCreature longCrit = defaultCreature
@@ -133,12 +121,31 @@ longCrit = defaultCreature
multGunCrit :: Creature multGunCrit :: Creature
multGunCrit = defaultCreature multGunCrit = defaultCreature
{ _crPict = basicCrPict red { _crPict = basicCrPict red
, _crUpdate = stateUpdate (twitchMissAI 300 350) -- , _crUpdate = stateUpdate (twitchMissAI 300 350)
, _crInv = IM.fromList [(0,multGun),(1,medkit 100)] , _crInv = IM.fromList [(0,multGun),(1,medkit 100)]
, _crInvSel = 0 , _crInvSel = 0
, _crRad = 10 , _crRad = 10
, _crState = defaultState {_goals = [[InitGuard]]} , _crState = defaultState {_goals = [[InitGuard]]}
, _crHP = 300 , _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 :: Creature
launcherCrit = defaultCreature launcherCrit = defaultCreature
@@ -173,7 +180,25 @@ pistolCrit = defaultCreature
autoCrit :: Creature autoCrit :: Creature
autoCrit = defaultCreature autoCrit = defaultCreature
{ _crPict = basicCrPict red { _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)] , _crInv = IM.fromList [(0,autoGun),(1,medkit 100)]
, _crInvSel = 0 , _crInvSel = 0
, _crRad = 10 , _crRad = 10
@@ -246,6 +271,7 @@ startCr = defaultCreature
, _crMaxHP = 1500 , _crMaxHP = 1500
, _crInv = startInventory , _crInv = startInventory
, _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ pictures [color (greyN 0.8) $ circleSolid 10, circLine 10] , _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ pictures [color (greyN 0.8) $ circleSolid 10, circLine 10]
, _crFaction = PlayerFaction
} }
{- | {- |
Items you start with. Items you start with.
@@ -253,6 +279,7 @@ Items you start with.
startInventory = IM.fromList (zip [0..20] startInventory = IM.fromList (zip [0..20]
( (
[bezierGun [bezierGun
,multGun
,hvAutoGun ,hvAutoGun
,remoteLauncher ,remoteLauncher
,grenade ,grenade
+8
View File
@@ -355,6 +355,14 @@ advanceStepCounter speed cr = over (crStance . carriage) f cr
creatureTurn :: Float -> Creature -> Creature creatureTurn :: Float -> Creature -> Creature
creatureTurn a = crDir +~ a 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 :: Point2 -> Float -> Creature -> Creature
creatureTurnToward p turnSpeed cr creatureTurnToward p turnSpeed cr
| vToTarg == (0,0) = cr -- this should deal with the angleVV error | 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 useItem n w = equippedItemEffect n w
crUseItem :: Creature -> World -> World 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 tryUseItem
:: Int -- ^ Creature id :: Int -- ^ Creature id
+194 -7
View File
@@ -1,25 +1,212 @@
module Dodge.Creature.ActionRat module Dodge.Creature.ActionRat
( shootTargetWithStrat
, suppressShootTarget
, suppress
, shootAdvance
, shootFirstMiss
, doStrategyActions
, reloadOverride
, shootAdvanceStrat
, watchUpdateStrat
, goToPostStrat
, overrideInternal
)
where where
import Dodge.Data import Dodge.Data
import Dodge.Base
import Dodge.Base.Collide import Dodge.Base.Collide
import Dodge.Creature.ChooseTarget import Dodge.Creature.ChooseTarget
import Dodge.Creature.Rationality.Data import Dodge.Creature.Rationality.Data
import Dodge.Creature.Stance.Data
import Dodge.Creature.Test
import Geometry import Geometry
import Data.List import Data.List
import qualified Data.IntMap.Strict as IM
import Control.Lens 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 -} {- | Action update for a simple shooting creature -}
shootAtTarget suppressShootTarget
:: (Creature -> World -> Maybe Creature) -- ^ Function for determining target :: (Creature -> World -> Maybe Creature) -- ^ Function for determining target
-> World -> World
-> Creature -> Creature
-> Creature -> Creature
shootAtTarget targFunc w cr = case targFunc cr w of suppressShootTarget targFunc w cr = case targFunc cr w of
Nothing -> cr 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] suppress :: World -> Creature -> Creature -> [Action] -> [Action]
shootAtTarg w cr tcr as suppress w cr tcr as
| canSee (_crID cr) (_crID tcr) w = nub (ShootTillEmpty : as) | canSee (_crID cr) (_crID tcr) w && cr ^. crStance . posture /= Aiming
| otherwise = as && 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 -> Creature
chaseTarget targFunc w cr = case targFunc cr w of chaseTarget targFunc w cr = case targFunc cr w of
Nothing -> cr Nothing -> cr
Just crTarg -> cr & crRationality . crImpulse .~ chaseTarg cr crTarg Just crTarg -> cr & crActionPlan . crImpulse .~ chaseTarg cr crTarg
chaseTarg :: Creature -> Creature -> [Impulse] chaseTarg :: Creature -> Creature -> [Impulse]
chaseTarg cr crT chaseTarg cr crT
@@ -40,7 +40,7 @@ impulseShootAtTarget
-> Creature -> Creature
impulseShootAtTarget targFunc w cr = case targFunc cr w of impulseShootAtTarget targFunc w cr = case targFunc cr w of
Nothing -> cr Nothing -> cr
Just crTarg -> cr & crRationality . crImpulse .~ impulseShootAtTarg cr crTarg Just crTarg -> cr & crActionPlan . crImpulse .~ impulseShootAtTarg cr crTarg
impulseShootAtTarg :: Creature -> Creature -> [Impulse] impulseShootAtTarg :: Creature -> Creature -> [Impulse]
impulseShootAtTarg cr crT impulseShootAtTarg cr crT
+1 -3
View File
@@ -20,7 +20,7 @@ import Picture
import qualified Data.IntMap.Strict as IM import qualified Data.IntMap.Strict as IM
import Control.Lens import Control.Lens
defaultInanimate = defaultCreature & crState . crIsAnimate .~ False defaultInanimate = defaultCreature & crActionPlan .~ Inanimate
lamp :: Creature lamp :: Creature
lamp = defaultInanimate lamp = defaultInanimate
@@ -60,7 +60,6 @@ barrel = defaultInanimate
] ]
, _crState = defaultState , _crState = defaultState
{_goals = [[Wait]] {_goals = [[Wait]]
,_faction = ChaseCritters
,_crSpState = Barrel [] ,_crSpState = Barrel []
} }
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)] , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
@@ -76,7 +75,6 @@ explosiveBarrel = defaultInanimate
] ]
, _crState = defaultState , _crState = defaultState
{_goals = [[Wait]] {_goals = [[Wait]]
,_faction = ChaseCritters
,_crSpState = Barrel [] ,_crSpState = Barrel []
} }
, _crInv = IM.empty -- IM.fromList [(0,frontArmour)] , _crInv = IM.empty -- IM.fromList [(0,frontArmour)]
+3 -2
View File
@@ -6,6 +6,7 @@ module Dodge.Creature.Picture
, circLine , circLine
) where ) where
import Dodge.Data import Dodge.Data
import Dodge.Creature.Stance.Data
import Dodge.Creature.State.Data import Dodge.Creature.State.Data
import Dodge.Creature.AlertLevel.Data import Dodge.Creature.AlertLevel.Data
import Dodge.Picture.Layer import Dodge.Picture.Layer
@@ -21,7 +22,7 @@ basicCrPict
-> Picture -> Picture
basicCrPict col cr = pictures basicCrPict col cr = pictures
[ onLayer CrLayer . piercingMod $ bluntScale naked [ onLayer CrLayer . piercingMod $ bluntScale naked
, drawAwakeLevel cr -- , drawAwakeLevel cr
, drawEquipment cr , drawEquipment cr
] ]
where where
@@ -66,7 +67,7 @@ drawEquipment
drawEquipment cr = pictures $ map f $ IM.toList (_crInv cr) drawEquipment cr = pictures $ map f $ IM.toList (_crInv cr)
where where
f (i,it) = case it ^? itEquipPict of f (i,it) = case it ^? itEquipPict of
Just g -> g cr i Just g | (cr ^? crStance . posture) == Just Aiming -> g cr i
_ -> blank _ -> blank
circLine x = line [(0,0),(x,0)] 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
import Dodge.Creature.Action.UseItem import Dodge.Creature.Action.UseItem
import Dodge.Creature.State.Data import Dodge.Creature.State.Data
import Dodge.Creature.Stance.Data
import Dodge.SoundLogic import Dodge.SoundLogic
import Geometry import Geometry
@@ -14,6 +15,8 @@ import qualified Data.IntMap.Strict as IM
import System.Random import System.Random
import Control.Lens import Control.Lens
-- Alternatives would probably be a very good fit for actions...
composeInternalAIs composeInternalAIs
:: [World -> Creature -> Creature] :: [World -> Creature -> Creature]
-> World -> World
@@ -39,7 +42,7 @@ followImpulses w (f,g) cr
$ foldr $ foldr
(\imp (f' , cr') -> let (f'', cr'') = followImpulse imp w cr' in (f'' . f', cr'')) (\imp (f' , cr') -> let (f'', cr'') = followImpulse imp w cr' in (f'' . f', cr''))
(f, cr) (f, cr)
(_crImpulse $ _crRationality cr) (_crImpulse $ _crActionPlan cr)
where where
g' = snd $ next g g' = snd $ next g
@@ -53,14 +56,20 @@ followImpulse imp w cr = case imp of
MoveForward x -> (id, crMvForward x cr) MoveForward x -> (id, crMvForward x cr)
Turn a -> (id, creatureTurn a cr) Turn a -> (id, creatureTurn a cr)
TurnToward p a -> (id, creatureTurnToward p 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) SwitchToItem i -> (id, cr & crInvSel .~ i)
Melee crID -> Melee crID ->
(hitCr crID (hitCr crID
, crMvBy (10 *.* normalizeV (posFromID crID -.- cpos)) $ cr & crMeleeCooldown ?~ 20) -- randomise cooldown? , crMvBy (10 *.* normalizeV (posFromID crID -.- cpos)) $ cr & crMeleeCooldown ?~ 20) -- randomise cooldown?
RandomTurn a -> (id, creatureTurn (rr a) cr) RandomTurn a -> (id, creatureTurn (rr a) cr)
_ -> (id , cr) MakeSound sid -> ( soundOnceOrigin sid (CrSound (_crID cr)) (_crPos cr) , cr )
DropItem -> undefined 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 where
cpos = _crPos cr cpos = _crPos cr
posFromID cid = _crPos $ _creatures w IM.! cid 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 :: World -> Creature -> Creature
performActions w cr = cr performActions w cr = cr
& crRationality . crImpulse .~ concat iss & crActionPlan . crImpulse .~ concat iss
& crRationality . crAction .~ catMaybes mayas & crActionPlan . crAction .~ catMaybes mayas
where 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 {- | Performing an action means that a creature has some impulses for a frame, and
updates or deletes the action itself. -} updates or deletes the action itself. -}
@@ -91,9 +100,9 @@ performAction
-> Action -> Action
-> ( [Impulse] , Maybe Action ) -> ( [Impulse] , Maybe Action )
performAction cr w ac = case ac of performAction cr w ac = case ac of
ShootTillEmpty -> case cr ^? crInv . ix (_crInvSel cr) . wpLoadedAmmo of ShootTillEmpty -> case cr ^? crInv . ix (_crInvSel cr) . wpReloadState of
Just x | x > 0 -> ( [UseItem] , Just ShootTillEmpty ) Just x | x == 0 -> ( [UseItem] , Just ShootTillEmpty )
_ -> ( [] , Nothing ) _ -> ( [] , Just (WaitThen 20 HolsterWeapon) )
AimAtCloseSlow tcid p speed slowSpeed a AimAtCloseSlow tcid p speed slowSpeed a
| canSee (_crID cr) tcid w && safeAngleVV (unitVectorAtAngle cdir) (tpos -.- cpos) < a | canSee (_crID cr) tcid w && safeAngleVV (unitVectorAtAngle cdir) (tpos -.- cpos) < a
-> ([TurnToward tpos slowSpeed] , Just $ AimAtCloseSlow tcid tpos speed slowSpeed 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) | otherwise -> ([TurnToward p speed] , Just $ AimAtCloseSlow tcid p speed slowSpeed a)
where where
tpos = _crPos (_creatures w IM.! tcid) 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 (xs:xss) -> (xs, Just $ ImpulsesList xss)
ImpulsesList _ -> ([], Nothing) 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 where
cpos = _crPos cr cpos = _crPos cr
cdir = _crDir cr cdir = _crDir cr
-61
View File
@@ -6,64 +6,3 @@ import Dodge.Creature.Stance.Data
import Geometry.Data import Geometry.Data
import Control.Lens 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 | otherwise = case cr ^? crStance . carriage of
Just (Walking x y) -> takeStep x y w Just (Walking x y) -> takeStep x y w
_ -> w _ -> w
where hasJetPack = any (\it -> it ^? itIdentity == Just JetPack) $ _crInv cr where
oldPos = _crOldPos cr hasJetPack = any (\it -> it ^? itIdentity == Just JetPack) $ _crInv cr
momentum' = 0.97 *.* (_crPos cr -.- _crOldPos cr) oldPos = _crOldPos cr
momentum'' | magV momentum' > 3 = 3 *.* normalizeV momentum' momentum' = 0.97 *.* (_crPos cr -.- _crOldPos cr)
| otherwise = momentum' momentum'' | magV momentum' > 3 = 3 *.* normalizeV momentum'
momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng | otherwise = momentum'
(randDir,g) = randomR (-0.5,0.5) $ _randGen w momentum = momentum'' +.+ 0.01 *.* unitVectorAtAngle randAng
(randAng,_) = randomR (0,2*pi) $ _randGen w (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 (v1:v2:v3:_) = fst $ runState ((sequence . repeat . randInCirc) 0.1) $ _randGen w
(r1:r2:r3:_) = map ((*.*) 100) (v2:v3:v1:[]) (r1:r2:r3:_) = map ((*.*) 100) (v2:v3:v1:[])
crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5 crHasMoved = dist (_crPos cr) (_crOldPos cr) > 0.5
takeStep x y | crHasMoved && x < 20 && x + y >= 20 = soundMultiFrom footor 22 3 0 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 | crHasMoved && x < 80 && x + y >= 80 = soundMultiFrom footor 23 3 0
| otherwise = id | otherwise = id
footor = [FootstepSound 0,FootstepSound 1] footor = [FootstepSound 0,FootstepSound 1]
invSideEff :: Creature -> World -> World invSideEff :: Creature -> World -> World
invSideEff cr w = weaponReloadSounds cr $ IM.foldrWithKey f w (_crInv cr) 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 :: Creature -> World -> World
weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of weaponReloadSounds cr w = case _crInv cr IM.!? _crInvSel cr of
Just (Weapon {_wpReloadState = 0}) Just (Weapon {_wpReloadState = 0}) -> w
-> w Just (Weapon {_wpReloadState = 1}) -> stopSoundFrom (CrReloadSound cid) w
Just (Weapon {_wpReloadState = 1}) Just (Weapon {}) -> soundFrom (CrReloadSound cid) reloadSound (1) 0 w
-> stopSoundFrom (CrReloadSound cid) w _ -> w
Just (Weapon {}) where
-> soundFrom (CrReloadSound cid) reloadSound (1) 0 w cid = _crID cr
_
-> w
where cid = _crID cr
updateMovement :: StdGen -> Creature -> (Creature, StdGen) updateMovement :: StdGen -> Creature -> (Creature, StdGen)
updateMovement g cr updateMovement g cr
| isFrictionless cr | isFrictionless cr = (over crPos (+.+ momentum) $ setOldPos cr, g')
= (over crPos (+.+ momentum) $ setOldPos cr, g') | otherwise = case cr ^? crStance . carriage of
| otherwise Just (Walking x y)
= case cr ^? crStance . carriage of -> (set (crStance . carriage) (Walking ((x+y)`mod`120) 0) $ setOldPos cr
Just (Walking x y) , g)
-> (set (crStance . carriage) (Walking ((x+y)`mod`120) 0) _ -> (set (crStance . carriage) (Walking 0 0) $ setOldPos cr
$ setOldPos , g)
cr, g)
_ -> (set (crStance . carriage) (Walking 0 0)
$ setOldPos
cr, g)
where where
momentum' = 0.98 *.* (_crPos cr -.- _crOldPos cr) momentum' = 0.98 *.* (_crPos cr -.- _crOldPos cr)
momentum'' | magV momentum' > 1 = 1 *.* normalizeV momentum' momentum'' | magV momentum' > 1 = 1 *.* normalizeV momentum'
@@ -180,25 +174,30 @@ isFrictionless cr = case cr ^? crStance . carriage of
updateReloadCounter :: Creature -> Creature updateReloadCounter :: Creature -> Creature
updateReloadCounter cr = over (crInv . ix iSel . itUseTime) decreaseToZero updateReloadCounter cr = over (crInv . ix iSel . itUseTime) decreaseToZero
. over (crInv . ix iSel . wpReloadState) decreaseToZero . over (crInv . ix iSel . wpReloadState) decreaseToZero
$ cr $ cr
where iSel = _crInvSel cr where
iSel = _crInvSel cr
decreaseToZero :: Int -> Int decreaseToZero :: Int -> Int
decreaseToZero x | x > 0 = x - 1 decreaseToZero x = max 0 (x - 1)
| otherwise = 0
onDeath :: (Creature -> World -> World) -> CRUpdate -> CRUpdate onDeath :: (Creature -> World -> World) -> CRUpdate -> CRUpdate
onDeath h u w (f,g) cr onDeath h u w (f,g) cr
| _crHP cr > 0 = u w (f,g) cr | _crHP cr > 0 = u w (f,g) cr
| otherwise = ( ( h cr . f, g ) , Nothing ) | otherwise = ( ( h cr . f, g ) , Nothing )
updateBarrel :: updateBarrel
World -> (World -> World,StdGen) -> Creature -> ((World -> World , StdGen), Maybe Creature) :: World
updateBarrel w (f,g) cr | _crHP cr > 0 = ((f, g), newCr) -> (World -> World,StdGen)
| otherwise = ((f, g), Nothing) -> Creature
where damages = _crDamage $ _crState cr -> ((World -> World , StdGen), Maybe Creature)
newCr = Just $ doDamage cr 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 -- it is easy to leave off the "f" here
-- should find some better way of doing all this that is less prone to error -- 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 updateExpBarrel w (f,g) cr
| _crHP cr > 0 = ((f . foldr (.) id pierceSparks . hiss, g'), newCr) | _crHP cr > 0 = ((f . foldr (.) id pierceSparks . hiss, g'), newCr)
| otherwise = ((f . makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing) | otherwise = ((f . makeExplosionAt (_crPos cr) . stopSounds , g'), Nothing)
where damages = _crDamage $ _crState cr where
pierceSparks :: [World -> World] damages = _crDamage $ _crState cr
pierceSparks pierceSparks :: [World -> World]
= zipWith4 (\p a colid time-> (createBarrelSpark time colid (_crPos cr +.+ p) (a + argV p)) pierceSparks
(Just $ _crID cr)) = zipWith4 (\p a colid time-> (createBarrelSpark time colid (_crPos cr +.+ p) (a + argV p))
poss as colids times (Just $ _crID cr))
as = randomRs (-0.7,0.7) $ g poss as colids times
colids = randomRs (0,11) $ g as = randomRs (-0.7,0.7) $ g
times = randomRs (2,5) $ g colids = randomRs (0,11) $ g
(g',_) = split g times = randomRs (2,5) $ g
poss = _piercedPoints $ _crSpState $ _crState cr (g',_) = split g
--newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages poss = _piercedPoints $ _crSpState $ _crState cr
newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr --newCr = Just $ doDamage $ applyFuseDamage cr -- $ foldr perforate cr damages
perforate :: DamageType -> Creature -> Creature newCr = Just $ applyFuseDamage $ set (crState . crDamage) [] $ damToExpBarrel damages cr
perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints) perforate :: DamageType -> Creature -> Creature
((:) $ int -.- _crPos cr) cr perforate (Piercing amount sp int ep) cr = over (crState . crSpState . piercedPoints)
perforate _ cr = cr ((:) $ int -.- _crPos cr) cr
applyFuseDamage cr = over crHP (\hp -> hp - length (_piercedPoints perforate _ cr = cr
$ _crSpState $ _crState cr)) applyFuseDamage cr = over crHP (\hp -> hp - length (_piercedPoints
cr $ _crSpState $ _crState cr))
hiss | poss == [] = id cr
| otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1 hiss | poss == [] = id
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1) | otherwise = soundMultiFrom [BarrelHiss 0,BarrelHiss 1] 41 50 1
stopSounds = stopSoundFrom (BarrelHiss 0) . stopSoundFrom (BarrelHiss 1)
damToExpBarrel :: [DamageType] -> Creature -> Creature damToExpBarrel :: [DamageType] -> Creature -> Creature
damToExpBarrel ds cr = foldr damToExpBarrel' (foldr damToExpBarrel' cr pierceDam) otherDam 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.Data.DamageType
import Dodge.Creature.Impulse.Data import Dodge.Creature.Impulse.Data
import Dodge.Creature.Stance.Data import Dodge.Creature.Stance.Data
import Picture.Data
import Control.Lens import Control.Lens
data CreatureState = CrSt data CreatureState = CrSt
{ _goals :: [[Impulse]] { _goals :: [[Impulse]]
, _faction :: Faction
, _crDamage :: [DamageType] , _crDamage :: [DamageType]
, _crPastDamage :: [[DamageType]] , _crPastDamage :: [[DamageType]]
, _crSpState :: CrSpState , _crSpState :: CrSpState
, _crDropsOnDeath :: CreatureDropType , _crDropsOnDeath :: CreatureDropType
, _crIsAnimate :: Bool
} }
data CreatureDropType data CreatureDropType
@@ -36,6 +35,8 @@ data Faction
| ChaseCritters | ChaseCritters
| SpawnedBy Int | SpawnedBy Int
| NoFaction | NoFaction
| ColorFaction Color
| PlayerFaction
deriving (Eq,Show) deriving (Eq,Show)
makeLenses ''CreatureState 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 TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StrictData #-} {-# LANGUAGE StrictData #-}
{-# LANGUAGE DeriveGeneric #-}
module Dodge.Data module Dodge.Data
( module Dodge.Data ( module Dodge.Data
, module Dodge.Data.DamageType , module Dodge.Data.DamageType
@@ -33,9 +34,11 @@ import Geometry.Data
import Sound.Data import Sound.Data
import qualified DoubleStack as DS import qualified DoubleStack as DS
import GHC.Generics
import Control.Lens import Control.Lens
import Control.Monad.State import Control.Monad.State
import System.Random import System.Random
import Data.Data
import Data.Graph.Inductive import Data.Graph.Inductive
import Data.Int (Int16) import Data.Int (Int16)
import qualified Data.Set as S import qualified Data.Set as S
@@ -151,11 +154,13 @@ data Creature = Creature
, _crCorpse :: Picture , _crCorpse :: Picture
, _crApplyDamage :: [DamageType] -> Creature -> (World -> World,Creature) , _crApplyDamage :: [DamageType] -> Creature -> (World -> World,Creature)
, _crStance :: Stance , _crStance :: Stance
, _crRationality :: Rationality , _crActionPlan :: ActionPlan
, _crMeleeCooldown :: Maybe Int , _crMeleeCooldown :: Maybe Int
, _crAwakeLevel :: AwakeLevel , _crAwakeLevel :: AwakeLevel
, _crAttentionDir :: AttentionDir , _crAttentionDir :: AttentionDir
, _crAwarenessLevel :: IM.IntMap AwarenessLevel , _crAwarenessLevel :: IM.IntMap AwarenessLevel
, _crFaction :: Faction
, _crTarget :: Maybe Int
} }
data WorldState data WorldState
= DoorNumOpen Int = DoorNumOpen Int
@@ -443,6 +448,138 @@ data ForceField = FF
} }
data FFState = FFDestroyable { _ffsHP :: Int } 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 ''World
makeLenses ''Cloud makeLenses ''Cloud
makeLenses ''Creature makeLenses ''Creature
@@ -460,6 +597,9 @@ makeLenses ''ForceField
makeLenses ''FFState makeLenses ''FFState
makeLenses ''PressPlate makeLenses ''PressPlate
makeLenses ''Button makeLenses ''Button
makeLenses ''ActionPlan
makeLenses ''Impulse
makeLenses ''Action
numColor :: Int -> Color numColor :: Int -> Color
numColor 0 = (1,0,0,1) 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 , _crCorpse = onLayer CorpseLayer $ color (greyN 0.5) $ circleSolid 10
, _crApplyDamage = defaultApplyDamage , _crApplyDamage = defaultApplyDamage
, _crStance = Stance {_carriage=Walking 0 0,_posture=AtEase} , _crStance = Stance {_carriage=Walking 0 0,_posture=AtEase}
, _crRationality = ImpulseRat [] , _crActionPlan = ActionPlan [] [] WatchAndWait [LiveLongAndProsper]
, _crMeleeCooldown = Nothing , _crMeleeCooldown = Nothing
, _crAwakeLevel = Vigilant , _crAwakeLevel = Vigilant
, _crAttentionDir = AttentiveTo [] , _crAttentionDir = AttentiveTo []
, _crAwarenessLevel = IM.empty , _crAwarenessLevel = IM.empty
, _crFaction = NoFaction
, _crTarget = Nothing
} }
defaultState = CrSt defaultState = CrSt
{ _goals = [] { _goals = []
, _faction = NoFaction
, _crDamage = [] , _crDamage = []
, _crPastDamage = [] , _crPastDamage = []
, _crSpState = GenCr , _crSpState = GenCr
, _crDropsOnDeath = DropAmount 1 , _crDropsOnDeath = DropAmount 1
, _crIsAnimate = True
} }
defaultEquipment = Equipment defaultEquipment = Equipment
{ _itIdentity = Generic { _itIdentity = Generic
+1 -1
View File
@@ -50,7 +50,7 @@ roomTreex = do
[[StartRoom] [[StartRoom]
,[Corridor] ,[Corridor]
,[SpecificRoom . pure . pure . Right $ roomGlassOctogon 400 ,[SpecificRoom . pure . pure . Right $ roomGlassOctogon 400
& rmPS %~ (PS (0,0) 0 (PutCrit miniGunCrit') :) & rmPS %~ (PS (0,-300) 0 (PutCrit multGunCrit) :)
] ]
,[Corridor] ,[Corridor]
,[Corridor] ,[Corridor]
+4 -5
View File
@@ -36,11 +36,10 @@ charFiringStrat
-> Int -- ^ Creature id -> Int -- ^ Creature id
-> World -> World
-> World -> World
charFiringStrat strats cid w = charFiringStrat strats cid w = case w ^? creatures . ix cid . crInv
let Just (c :<| _) = w ^? creatures . ix cid . crInv . ix (_crInvSel $ _creatures w IM.! cid) . itAttachment . _Just . itCharMode of
. ix (_crInvSel $ _creatures w IM.! cid) . itAttachment Just (c :<| _) -> fromJust (Prelude.lookup c strats) cid w
. _Just . itCharMode Nothing -> w
in fromJust (Prelude.lookup c strats) cid w
increaseFuse increaseFuse
:: Int -- ^ Old fuse time :: Int -- ^ Old fuse time
+1 -1
View File
@@ -96,7 +96,7 @@ autoDoorPane (trigx,trigy) n closedPos openPos = Door
a = fst closedPos a = fst closedPos
b = snd closedPos b = snd closedPos
dm w 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 = flip (foldr changeZonedWall) zoneps $ over walls (IM.adjust openDoor n) w
| otherwise | otherwise
= flip (foldr changeZonedWall') zoneps $ over walls (IM.adjust closeDoor n) w = flip (foldr changeZonedWall') zoneps $ over walls (IM.adjust closeDoor n) w
+2 -23
View File
@@ -42,8 +42,10 @@ module Dodge.SoundLogic (
, smokeTrailSound , smokeTrailSound
, foot1Sound , foot1Sound
, foot2Sound , foot2Sound
, module Dodge.SoundLogic.Synonyms
) where ) where
import Dodge.Data import Dodge.Data
import Dodge.SoundLogic.Synonyms
import Sound.Data (SoundStatus (..)) import Sound.Data (SoundStatus (..))
import Geometry.Vector import Geometry.Vector
import Geometry (dist) import Geometry (dist)
@@ -180,26 +182,3 @@ soundMultiFrom (so:sos) sType time fadeTime w
stopSoundFrom :: SoundOrigin -> World -> World stopSoundFrom :: SoundOrigin -> World -> World
stopSoundFrom so = over (sounds . ix so . soundTime) (fmap $ min 0) 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 module Dodge.WallCreatureCollisions where
-- imports {{{
import Dodge.Data import Dodge.Data
import Dodge.Creature.State.Data import Dodge.Creature.State.Data
import Dodge.Base import Dodge.Base
@@ -43,12 +45,11 @@ wallBuffer = 3
-- out from the wall -- out from the wall
-- this is then repeated if the point ends up on a new wall -- this is then repeated if the point ends up on a new wall
collideWalls :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2 collideWalls :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2
collideWalls rad cp1 walls cp2 collideWalls rad cp1 walls cp2 = case (listToMaybe.mapMaybe (pushOutFromWall rad cp2)) walls of
= case (listToMaybe.mapMaybe (pushOutFromWall rad cp2)) walls of Nothing -> cp2
Nothing -> cp2 Just cp3 -> case (listToMaybe.reverse.mapMaybe (pushOutFromWall rad cp3)) walls of
Just cp3 -> case (listToMaybe.reverse.mapMaybe (pushOutFromWall rad cp3)) walls of Nothing -> cp3
Nothing -> cp3 Just cp4 -> cp1
Just cp4 -> cp4
-- pushes a point out from a list of walls -- pushes a point out from a list of walls
-- if multiple new points occur, chooses the one closest to the orignal point -- 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 -- assumes wall points are different
pushOutFromWall :: Float -> Point2 -> (Point2,Point2) -> Maybe (Point2) pushOutFromWall :: Float -> Point2 -> (Point2,Point2) -> Maybe (Point2)
pushOutFromWall rad cp2 (wp1,wp2) pushOutFromWall rad cp2 (wp1,wp2)
| isOnWall = Just newP -- +.+ (1 *.* norm)) | isOnWall = Just newP -- +.+ (1 *.* norm))
| otherwise = Nothing | otherwise = Nothing
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2) where
wp1' = (rad *.* norm) +.+ wp1 norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp2' = (rad *.* norm) +.+ wp2 wp1' = (rad *.* norm) +.+ wp1
newP = errorClosestPointOnLine 5 wp1' wp2' cp2 wp2' = (rad *.* norm) +.+ wp2
isOnWall = circOnSegNoEndpoints wp1 wp2 cp2 rad newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isJust Nothing = False isOnWall = circOnSegNoEndpoints wp1 wp2 cp2 rad
isJust _ = True isJust Nothing = False
isJust _ = True
pushOutFromCorners :: World -> Creature -> Creature pushOutFromCorners :: World -> Creature -> Creature
pushOutFromCorners w cr = cr & crPos .~ newPos 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) -- collide circles with points (outer corners)
intersectCirclePoint :: Float -> Point2 -> Point2 -> Point2 intersectCirclePoint :: Float -> Point2 -> Point2 -> Point2
intersectCirclePoint rad p cCen | dist cCen p > rad = cCen intersectCirclePoint rad p cCen
| otherwise = p +.+ (rad *.* errorNormalizeV 65 (cCen -.- p)) | dist cCen p > rad = cCen
| otherwise = p +.+ (rad *.* errorNormalizeV 65 (cCen -.- p))
checkPushThroughs :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2 checkPushThroughs :: Float -> Point2 -> [(Point2,Point2)] -> Point2 -> Point2
checkPushThroughs rad cp1 walls cp2 checkPushThroughs rad cp1 walls cp2
@@ -102,12 +105,13 @@ checkPushThroughs rad cp1 walls cp2
checkPushThrough :: Float -> Point2 -> Point2 -> (Point2,Point2) -> Maybe (Point2) checkPushThrough :: Float -> Point2 -> Point2 -> (Point2,Point2) -> Maybe (Point2)
checkPushThrough rad cp1 cp2 (wp1,wp2) checkPushThrough rad cp1 cp2 (wp1,wp2)
| isPushedThrough = intersectSegSeg' cp1 cp2 wp1 wp2 | isPushedThrough = intersectSegSeg' cp1 cp2 wp1 wp2
| otherwise = Nothing | otherwise = Nothing
where norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2) where
wp1' = (rad *.* norm) +.+ wp1 norm = errorNormalizeV 61 $ vNormal (wp1 -.- wp2)
wp2' = (rad *.* norm) +.+ wp2 wp1' = (rad *.* norm) +.+ wp1
newP = errorClosestPointOnLine 5 wp1' wp2' cp2 wp2' = (rad *.* norm) +.+ wp2
isPushedThrough = isRHS wp1 wp2 cp2 && isJust (intersectSegSeg' cp1 cp2 wp1 wp2) newP = errorClosestPointOnLine 5 wp1' wp2' cp2
isJust Nothing = False isPushedThrough = isRHS wp1 wp2 cp2 && isJust (intersectSegSeg' cp1 cp2 wp1 wp2)
isJust _ = True 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 -- | Test whether an angle is to the left of another angle, according to the
-- smallest change in rotation between them. -- smallest change in rotation between them.
-- This appears to sometimes fail if the angles are not normalized.
isLeftOfA :: Float -> Float -> Bool isLeftOfA :: Float -> Float -> Bool
isLeftOfA angle1 angle2 = isLeftOfA angle1 angle2 =
(angle1 - angle2 < pi && 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