{-# LANGUAGE TupleSections #-}
-- | Let AI pick the best target for an actor.
module Game.LambdaHack.Client.AI.PickTargetM
  ( refreshTarget
#ifdef EXPOSE_INTERNAL
    -- * Internal operations
  , computeTarget
#endif
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES

import           Game.LambdaHack.Client.AI.ConditionM
import           Game.LambdaHack.Client.Bfs
import           Game.LambdaHack.Client.BfsM
import           Game.LambdaHack.Client.CommonM
import           Game.LambdaHack.Client.MonadClient
import           Game.LambdaHack.Client.State
import           Game.LambdaHack.Common.Actor
import           Game.LambdaHack.Common.ActorState
import           Game.LambdaHack.Common.Faction
import           Game.LambdaHack.Common.Item
import           Game.LambdaHack.Common.Kind
import           Game.LambdaHack.Common.Level
import           Game.LambdaHack.Common.MonadStateRead
import           Game.LambdaHack.Common.Point
import qualified Game.LambdaHack.Common.PointArray as PointArray
import           Game.LambdaHack.Common.State
import qualified Game.LambdaHack.Common.Tile as Tile
import           Game.LambdaHack.Common.Time
import           Game.LambdaHack.Common.Types
import           Game.LambdaHack.Common.Vector
import           Game.LambdaHack.Content.ModeKind
import           Game.LambdaHack.Content.RuleKind
import           Game.LambdaHack.Content.TileKind (isUknownSpace)
import           Game.LambdaHack.Core.Frequency
import           Game.LambdaHack.Core.Random
import qualified Game.LambdaHack.Definition.Ability as Ability

-- | Verify and possibly change the target of an actor. This function both
-- updates the target in the client state and returns the new target explicitly.
refreshTarget :: MonadClient m => (ActorId, Actor) -> m (Maybe TgtAndPath)
-- This inline would speeds up execution by 5% and decreases allocation by 10%,
-- but it'd bloat JS code without speeding it up.
-- {-# INLINE refreshTarget #-}
refreshTarget :: (ActorId, Actor) -> m (Maybe TgtAndPath)
refreshTarget (aid :: ActorId
aid, body :: Actor
body) = do
  FactionId
side <- (StateClient -> FactionId) -> m FactionId
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient StateClient -> FactionId
sside
  let !_A :: ()
_A = Bool -> () -> ()
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Actor -> FactionId
bfid Actor
body FactionId -> FactionId -> Bool
forall a. Eq a => a -> a -> Bool
== FactionId
side
                    Bool -> (String, (ActorId, Actor, FactionId)) -> Bool
forall a. Show a => Bool -> a -> Bool
`blame` "AI tries to move an enemy actor"
                    String
-> (ActorId, Actor, FactionId)
-> (String, (ActorId, Actor, FactionId))
forall v. String -> v -> (String, v)
`swith` (ActorId
aid, Actor
body, FactionId
side)) ()
  let !_A :: ()
_A = Bool -> () -> ()
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Bool -> Bool
not (Actor -> Bool
bproj Actor
body)
                    Bool -> (String, (ActorId, Actor, FactionId)) -> Bool
forall a. Show a => Bool -> a -> Bool
`blame` "AI gets to manually move its projectiles"
                    String
-> (ActorId, Actor, FactionId)
-> (String, (ActorId, Actor, FactionId))
forall v. String -> v -> (String, v)
`swith` (ActorId
aid, Actor
body, FactionId
side)) ()
  Maybe TgtAndPath
mtarget <- ActorId -> m (Maybe TgtAndPath)
forall (m :: * -> *).
MonadClient m =>
ActorId -> m (Maybe TgtAndPath)
computeTarget ActorId
aid
  case Maybe TgtAndPath
mtarget of
    Nothing -> do
      -- Melee in progress and the actor can't contribute
      -- and would slow down others if he acted.
      -- Or he's just asleep.
      (StateClient -> StateClient) -> m ()
forall (m :: * -> *).
MonadClient m =>
(StateClient -> StateClient) -> m ()
modifyClient ((StateClient -> StateClient) -> m ())
-> (StateClient -> StateClient) -> m ()
forall a b. (a -> b) -> a -> b
$ \cli :: StateClient
cli -> StateClient
cli {stargetD :: EnumMap ActorId TgtAndPath
stargetD = ActorId -> EnumMap ActorId TgtAndPath -> EnumMap ActorId TgtAndPath
forall k a. Enum k => k -> EnumMap k a -> EnumMap k a
EM.delete ActorId
aid (StateClient -> EnumMap ActorId TgtAndPath
stargetD StateClient
cli)}
      Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe TgtAndPath
forall a. Maybe a
Nothing
    Just tgtMPath :: TgtAndPath
tgtMPath -> do
      -- _debugoldTgt <- getsClient $ EM.lookup aid . stargetD
      -- Choose a target from those proposed by AI for the actor.
      (StateClient -> StateClient) -> m ()
forall (m :: * -> *).
MonadClient m =>
(StateClient -> StateClient) -> m ()
modifyClient ((StateClient -> StateClient) -> m ())
-> (StateClient -> StateClient) -> m ()
forall a b. (a -> b) -> a -> b
$ \cli :: StateClient
cli ->
        StateClient
cli {stargetD :: EnumMap ActorId TgtAndPath
stargetD = ActorId
-> TgtAndPath
-> EnumMap ActorId TgtAndPath
-> EnumMap ActorId TgtAndPath
forall k a. Enum k => k -> a -> EnumMap k a -> EnumMap k a
EM.insert ActorId
aid TgtAndPath
tgtMPath (StateClient -> EnumMap ActorId TgtAndPath
stargetD StateClient
cli)}
      Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe TgtAndPath
mtarget
      -- let _debug = T.unpack
      --       $ "\nHandleAI symbol:"    <+> tshow (bsymbol body)
      --       <> ", aid:"               <+> tshow aid
      --       <> ", pos:"               <+> tshow (bpos body)
      --       <> "\nHandleAI oldTgt:"   <+> tshow _debugoldTgt
      --       <> "\nHandleAI strTgt:"   <+> tshow stratTarget
      --       <> "\nHandleAI target:"   <+> tshow tgtMPath
      -- trace _debug $ return $ Just tgtMPath

computeTarget :: forall m. MonadClient m => ActorId -> m (Maybe TgtAndPath)
{-# INLINE computeTarget #-}
computeTarget :: ActorId -> m (Maybe TgtAndPath)
computeTarget aid :: ActorId
aid = do
  cops :: COps
cops@COps{corule :: COps -> RuleContent
corule=RuleContent{X
rXmax :: RuleContent -> X
rXmax :: X
rXmax, X
rYmax :: RuleContent -> X
rYmax :: X
rYmax, X
rnearby :: RuleContent -> X
rnearby :: X
rnearby}, TileSpeedup
coTileSpeedup :: COps -> TileSpeedup
coTileSpeedup :: TileSpeedup
coTileSpeedup}
    <- (State -> COps) -> m COps
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState State -> COps
scops
  Actor
b <- (State -> Actor) -> m Actor
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Actor) -> m Actor) -> (State -> Actor) -> m Actor
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
aid
  Maybe ActorId
mleader <- (StateClient -> Maybe ActorId) -> m (Maybe ActorId)
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient StateClient -> Maybe ActorId
sleader
  AlterLid
salter <- (StateClient -> AlterLid) -> m AlterLid
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient StateClient -> AlterLid
salter
  -- We assume the actor eventually becomes a leader (or has the same
  -- set of skills as the leader, anyway) and set his target accordingly.
  ActorMaxSkills
actorMaxSkills <- (State -> ActorMaxSkills) -> m ActorMaxSkills
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState State -> ActorMaxSkills
sactorMaxSkills
  Bool
condInMelee <- LevelId -> m Bool
forall (m :: * -> *). MonadClient m => LevelId -> m Bool
condInMeleeM (LevelId -> m Bool) -> LevelId -> m Bool
forall a b. (a -> b) -> a -> b
$ Actor -> LevelId
blid Actor
b
  let lalter :: Array Word8
lalter = AlterLid
salter AlterLid -> LevelId -> Array Word8
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
b
      actorMaxSk :: Skills
actorMaxSk = ActorMaxSkills
actorMaxSkills ActorMaxSkills -> ActorId -> Skills
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ActorId
aid
      alterSkill :: X
alterSkill = Skill -> Skills -> X
Ability.getSk Skill
Ability.SkAlter Skills
actorMaxSk
  Level
lvl <- LevelId -> m Level
forall (m :: * -> *). MonadStateRead m => LevelId -> m Level
getLevel (LevelId -> m Level) -> LevelId -> m Level
forall a b. (a -> b) -> a -> b
$ Actor -> LevelId
blid Actor
b
  let stepAccesible :: [Point] -> Bool
      stepAccesible :: [Point] -> Bool
stepAccesible (q :: Point
q : _) =
        -- Effectively, only @alterMinWalk@ is checked, because real altering
        -- is not done via target path, but action after end of path.
        X
alterSkill X -> X -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8 -> X
forall a. Enum a => a -> X
fromEnum (Array Word8
lalter Array Word8 -> Point -> Word8
forall c. UnboxRepClass c => Array c -> Point -> c
PointArray.! Point
q)
      stepAccesible [] = Bool
False
  Maybe TgtAndPath
mtgtMPath <- (StateClient -> Maybe TgtAndPath) -> m (Maybe TgtAndPath)
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient ((StateClient -> Maybe TgtAndPath) -> m (Maybe TgtAndPath))
-> (StateClient -> Maybe TgtAndPath) -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ ActorId -> EnumMap ActorId TgtAndPath -> Maybe TgtAndPath
forall k a. Enum k => k -> EnumMap k a -> Maybe a
EM.lookup ActorId
aid (EnumMap ActorId TgtAndPath -> Maybe TgtAndPath)
-> (StateClient -> EnumMap ActorId TgtAndPath)
-> StateClient
-> Maybe TgtAndPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StateClient -> EnumMap ActorId TgtAndPath
stargetD
  Maybe TgtAndPath
oldTgtUpdatedPath <- case Maybe TgtAndPath
mtgtMPath of
    Just TgtAndPath{Target
tapTgt :: TgtAndPath -> Target
tapTgt :: Target
tapTgt,tapPath :: TgtAndPath -> Maybe AndPath
tapPath=Maybe AndPath
Nothing} ->
      -- This case is especially for TEnemyPos that would be lost otherwise.
      -- This is also triggered by @UpdLeadFaction@.
      TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just (TgtAndPath -> Maybe TgtAndPath)
-> m TgtAndPath -> m (Maybe TgtAndPath)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ActorId -> Target -> m TgtAndPath
forall (m :: * -> *).
MonadClient m =>
ActorId -> Target -> m TgtAndPath
createPath ActorId
aid Target
tapTgt
    Just tap :: TgtAndPath
tap@TgtAndPath{Target
tapTgt :: Target
tapTgt :: TgtAndPath -> Target
tapTgt,tapPath :: TgtAndPath -> Maybe AndPath
tapPath=Just AndPath{..}} -> do
      Maybe Point
mvalidPos <- (State -> Maybe Point) -> m (Maybe Point)
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Maybe Point) -> m (Maybe Point))
-> (State -> Maybe Point) -> m (Maybe Point)
forall a b. (a -> b) -> a -> b
$ ActorId -> LevelId -> Maybe Target -> State -> Maybe Point
aidTgtToPos ActorId
aid (Actor -> LevelId
blid Actor
b) (Target -> Maybe Target
forall a. a -> Maybe a
Just Target
tapTgt)
      Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$!
        if | Maybe Point -> Bool
forall a. Maybe a -> Bool
isNothing Maybe Point
mvalidPos -> Maybe TgtAndPath
forall a. Maybe a
Nothing  -- wrong level
           | Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Point
pathGoal->
               Maybe TgtAndPath
mtgtMPath  -- goal reached; stay there picking up items
           | Point
pathSource Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Actor -> Point
bpos Actor
b ->  -- no move
               -- If next step not accessible, something serious happened,
               -- so reconsider the target, not only path.
               if [Point] -> Bool
stepAccesible [Point]
pathList then Maybe TgtAndPath
mtgtMPath else Maybe TgtAndPath
forall a. Maybe a
Nothing
           | Bool
otherwise -> case (Point -> Bool) -> [Point] -> ([Point], [Point])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Actor -> Point
bpos Actor
b) [Point]
pathList of
               (crossed :: [Point]
crossed, _ : rest :: [Point]
rest) ->  -- step or many steps along path
                 if [Point] -> Bool
forall a. [a] -> Bool
null [Point]
rest
                 then Maybe TgtAndPath
forall a. Maybe a
Nothing  -- path to the goal was partial, so tiles
                               -- discovered or altered, so reconsider target
                 else let newPath :: AndPath
newPath =
                            $WAndPath :: Point -> [Point] -> Point -> X -> AndPath
AndPath{ pathSource :: Point
pathSource = Actor -> Point
bpos Actor
b
                                   , pathList :: [Point]
pathList = [Point]
rest
                                   , Point
pathGoal :: Point
pathGoal :: Point
pathGoal
                                   , pathLen :: X
pathLen = X
pathLen X -> X -> X
forall a. Num a => a -> a -> a
- [Point] -> X
forall a. [a] -> X
length [Point]
crossed X -> X -> X
forall a. Num a => a -> a -> a
- 1 }
                      in if [Point] -> Bool
stepAccesible [Point]
rest
                         then TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap{tapPath :: Maybe AndPath
tapPath=AndPath -> Maybe AndPath
forall a. a -> Maybe a
Just AndPath
newPath}
                         else Maybe TgtAndPath
forall a. Maybe a
Nothing
               (_, []) -> Maybe TgtAndPath
forall a. Maybe a
Nothing  -- veered off the path, e.g., due to push
                                   -- by enemy or congestion, so serious,
                                   -- so reconsider target, not only path
    Nothing -> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe TgtAndPath
forall a. Maybe a
Nothing  -- no target assigned yet
  Faction
fact <- (State -> Faction) -> m Faction
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Faction) -> m Faction)
-> (State -> Faction) -> m Faction
forall a b. (a -> b) -> a -> b
$ (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> FactionId
bfid Actor
b) (EnumMap FactionId Faction -> Faction)
-> (State -> EnumMap FactionId Faction) -> State -> Faction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> EnumMap FactionId Faction
sfactionD
  [(ActorId, Actor)]
allFoes <- (State -> [(ActorId, Actor)]) -> m [(ActorId, Actor)]
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> [(ActorId, Actor)]) -> m [(ActorId, Actor)])
-> (State -> [(ActorId, Actor)]) -> m [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ FactionId -> LevelId -> State -> [(ActorId, Actor)]
foeRegularAssocs (Actor -> FactionId
bfid Actor
b) (Actor -> LevelId
blid Actor
b)
  let canMove :: Bool
canMove = Skill -> Skills -> X
Ability.getSk Skill
Ability.SkMove Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 0
                Bool -> Bool -> Bool
|| Skill -> Skills -> X
Ability.getSk Skill
Ability.SkDisplace Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 0
                -- Needed for now, because AI targets and shoots enemies
                -- based on the path to them, not LOS to them:
                Bool -> Bool -> Bool
|| Skill -> Skills -> X
Ability.getSk Skill
Ability.SkProject Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 0
      canAlter :: Bool
canAlter = Skill -> Skills -> X
Ability.getSk Skill
Ability.SkAlter Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
>= 4
  Skills
actorMinSk <- (State -> Skills) -> m Skills
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Skills) -> m Skills) -> (State -> Skills) -> m Skills
forall a b. (a -> b) -> a -> b
$ Maybe ActorId -> ActorId -> State -> Skills
actorCurrentSkills Maybe ActorId
forall a. Maybe a
Nothing ActorId
aid
  Bool
condCanProject <-
    X -> ActorId -> m Bool
forall (m :: * -> *). MonadClient m => X -> ActorId -> m Bool
condCanProjectM (Skill -> Skills -> X
Ability.getSk Skill
Ability.SkProject Skills
actorMaxSk) ActorId
aid
  let condCanMelee :: Bool
condCanMelee = ActorMaxSkills -> ActorId -> Actor -> Bool
actorCanMelee ActorMaxSkills
actorMaxSkills ActorId
aid Actor
b
      condHpTooLow :: Bool
condHpTooLow = Actor -> Skills -> Bool
hpTooLow Actor
b Skills
actorMaxSk
  [Actor]
friends <- (State -> [Actor]) -> m [Actor]
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> [Actor]) -> m [Actor])
-> (State -> [Actor]) -> m [Actor]
forall a b. (a -> b) -> a -> b
$ FactionId -> LevelId -> State -> [Actor]
friendRegularList (Actor -> FactionId
bfid Actor
b) (Actor -> LevelId
blid Actor
b)
  let canEscape :: Bool
canEscape = Player -> Bool
fcanEscape (Faction -> Player
gplayer Faction
fact)
      canSmell :: Bool
canSmell = Skill -> Skills -> X
Ability.getSk Skill
Ability.SkSmell Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 0
      meleeNearby :: X
meleeNearby | Bool
canEscape = X
rnearby X -> X -> X
forall a. Integral a => a -> a -> a
`div` 2
                  | Bool
otherwise = X
rnearby
      rangedNearby :: X
rangedNearby = 2 X -> X -> X
forall a. Num a => a -> a -> a
* X
meleeNearby
      -- Don't target nonmoving actors, including sleeping, unless
      -- they have loot or attack ours or at heroes, because nonmoving
      -- can't be lured nor ambushed nor can chase us.
      --
      -- This is KISS, but not ideal, because AI doesn't fling at nonmoving
      -- actors but only at moving ones and so probably doesn't use
      -- ranged combat as much as would be optimal.
      worthTargetting :: ActorId -> Actor -> m Bool
worthTargetting aidE :: ActorId
aidE body :: Actor
body = do
        Skills
actorMaxSkE <- (State -> Skills) -> m Skills
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Skills) -> m Skills) -> (State -> Skills) -> m Skills
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Skills
getActorMaxSkills ActorId
aidE
        Faction
factE <- (State -> Faction) -> m Faction
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Faction) -> m Faction)
-> (State -> Faction) -> m Faction
forall a b. (a -> b) -> a -> b
$ (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> FactionId
bfid Actor
body) (EnumMap FactionId Faction -> Faction)
-> (State -> EnumMap FactionId Faction) -> State -> Faction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> EnumMap FactionId Faction
sfactionD
        let attacksFriends :: Bool
attacksFriends = (Actor -> Bool) -> [Actor] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Point -> Point -> Bool
adjacent (Actor -> Point
bpos Actor
body) (Point -> Bool) -> (Actor -> Point) -> Actor -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Actor -> Point
bpos) [Actor]
friends
            nonmoving :: Bool
nonmoving = Skill -> Skills -> X
Ability.getSk Skill
Ability.SkMove Skills
actorMaxSkE X -> X -> Bool
forall a. Ord a => a -> a -> Bool
<= 0
                        Bool -> Bool -> Bool
&& Actor -> Watchfulness
bwatch Actor
body Watchfulness -> Watchfulness -> Bool
forall a. Eq a => a -> a -> Bool
/= Watchfulness
WWake  -- will start moving very soon
            hasLoot :: Bool
hasLoot = Bool -> Bool
not (EnumMap ItemId ItemQuant -> Bool
forall k a. EnumMap k a -> Bool
EM.null (Actor -> EnumMap ItemId ItemQuant
beqp Actor
body)) Bool -> Bool -> Bool
|| Bool -> Bool
not (EnumMap ItemId ItemQuant -> Bool
forall k a. EnumMap k a -> Bool
EM.null (Actor -> EnumMap ItemId ItemQuant
binv Actor
body))
              -- even consider "unreported inventory", for speed and KISS
            isHero :: Bool
isHero = Player -> Bool
fhasGender (Faction -> Player
gplayer Faction
factE)
        Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$! Bool -> Bool
not Bool
nonmoving Bool -> Bool -> Bool
|| Bool
hasLoot Bool -> Bool -> Bool
|| Bool
attacksFriends Bool -> Bool -> Bool
|| Bool
isHero
      targetableMelee :: Actor -> Bool
targetableMelee body :: Actor
body =
        let attacksFriends :: Bool
attacksFriends = (Actor -> Bool) -> [Actor] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Point -> Point -> Bool
adjacent (Actor -> Point
bpos Actor
body) (Point -> Bool) -> (Actor -> Point) -> Actor -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Actor -> Point
bpos) [Actor]
friends
            -- 3 is
            -- 1 from condSupport1
            -- + 2 from foe being 2 away from friend before he closed in
            -- + 1 for as a margin for ambush, given than actors exploring
            -- can't physically keep adjacent all the time
            n :: X
n | Skill -> Skills -> X
Ability.getSk Skill
Ability.SkAggression Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
>= 2
              = X
rangedNearby
                  -- boss never waits
              | Bool
condInMelee = if Bool
attacksFriends then 4 else 2
                  -- attack even if foe not in melee, to create another
                  -- skirmish and perhaps overwhelm them in this one;
                  -- also, this looks more natural; also sometimes the foe
                  -- would attack our friend in a couple of turns anyway,
                  -- but we may be too far from him at that time
              | Bool
otherwise = X
meleeNearby
        in Bool
condCanMelee Bool -> Bool -> Bool
&& Point -> Point -> X
chessDist (Actor -> Point
bpos Actor
body) (Actor -> Point
bpos Actor
b) X -> X -> Bool
forall a. Ord a => a -> a -> Bool
<= X
n
      -- Even when missiles run out, the non-moving foe will still be
      -- targeted, which is fine, since he is weakened by ranged, so should be
      -- meleed ASAP, even if without friends.
      targetableRanged :: Actor -> Bool
targetableRanged body :: Actor
body =
        (Bool -> Bool
not Bool
condInMelee Bool -> Bool -> Bool
|| Skill -> Skills -> X
Ability.getSk Skill
Ability.SkAggression Skills
actorMaxSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
>= 2)
          -- boss fires at will
        Bool -> Bool -> Bool
&& Point -> Point -> X
chessDist (Actor -> Point
bpos Actor
body) (Actor -> Point
bpos Actor
b) X -> X -> Bool
forall a. Ord a => a -> a -> Bool
< X
rangedNearby
        Bool -> Bool -> Bool
&& Bool
condCanProject
      targetableEnemy :: (ActorId, Actor) -> m Bool
targetableEnemy (aidE :: ActorId
aidE, body :: Actor
body) =
        if Point -> Point -> Bool
adjacent (Actor -> Point
bpos Actor
body) (Actor -> Point
bpos Actor
b)
        then Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True  -- target regardless of anything, e.g., to flee
        else do
          Bool
worth <- ActorId -> Actor -> m Bool
worthTargetting ActorId
aidE Actor
body
          Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> m Bool) -> Bool -> m Bool
forall a b. (a -> b) -> a -> b
$! Bool
worth Bool -> Bool -> Bool
&& (Actor -> Bool
targetableRanged Actor
body Bool -> Bool -> Bool
|| Actor -> Bool
targetableMelee Actor
body)
  [(ActorId, Actor)]
nearbyFoes <- ((ActorId, Actor) -> m Bool)
-> [(ActorId, Actor)] -> m [(ActorId, Actor)]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM (ActorId, Actor) -> m Bool
targetableEnemy [(ActorId, Actor)]
allFoes
  DiscoveryBenefit
discoBenefit <- (StateClient -> DiscoveryBenefit) -> m DiscoveryBenefit
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient StateClient -> DiscoveryBenefit
sdiscoBenefit
  EnumMap ActorId Point
fleeD <- (StateClient -> EnumMap ActorId Point) -> m (EnumMap ActorId Point)
forall (m :: * -> *) a.
MonadClientRead m =>
(StateClient -> a) -> m a
getsClient StateClient -> EnumMap ActorId Point
sfleeD
  ItemId -> ItemKind
getKind <- (State -> ItemId -> ItemKind) -> m (ItemId -> ItemKind)
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> ItemId -> ItemKind) -> m (ItemId -> ItemKind))
-> (State -> ItemId -> ItemKind) -> m (ItemId -> ItemKind)
forall a b. (a -> b) -> a -> b
$ (ItemId -> State -> ItemKind) -> State -> ItemId -> ItemKind
forall a b c. (a -> b -> c) -> b -> a -> c
flip ItemId -> State -> ItemKind
getIidKind
  ItemId -> AspectRecord
getArItem <- (State -> ItemId -> AspectRecord) -> m (ItemId -> AspectRecord)
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> ItemId -> AspectRecord) -> m (ItemId -> AspectRecord))
-> (State -> ItemId -> AspectRecord) -> m (ItemId -> AspectRecord)
forall a b. (a -> b) -> a -> b
$ (ItemId -> State -> AspectRecord)
-> State -> ItemId -> AspectRecord
forall a b c. (a -> b -> c) -> b -> a -> c
flip ItemId -> State -> AspectRecord
aspectRecordFromIid
  let desirableIid :: (ItemId, ItemQuant) -> Bool
desirableIid (iid :: ItemId
iid, (k :: X
k, _)) =
        let Benefit{Double
benPickup :: Benefit -> Double
benPickup :: Double
benPickup} = DiscoveryBenefit
discoBenefit DiscoveryBenefit -> ItemId -> Benefit
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ItemId
iid
        in COps -> Bool -> Double -> AspectRecord -> ItemKind -> X -> Bool
desirableItem COps
cops Bool
canEscape Double
benPickup
                         (ItemId -> AspectRecord
getArItem ItemId
iid) (ItemId -> ItemKind
getKind ItemId
iid) X
k
      desirableBagFloor :: EnumMap ItemId ItemQuant -> Bool
desirableBagFloor bag :: EnumMap ItemId ItemQuant
bag = ((ItemId, ItemQuant) -> Bool) -> [(ItemId, ItemQuant)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (ItemId, ItemQuant) -> Bool
desirableIid ([(ItemId, ItemQuant)] -> Bool) -> [(ItemId, ItemQuant)] -> Bool
forall a b. (a -> b) -> a -> b
$ EnumMap ItemId ItemQuant -> [(ItemId, ItemQuant)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs EnumMap ItemId ItemQuant
bag
      desirableFloor :: (X, (Point, EnumMap ItemId ItemQuant)) -> Bool
desirableFloor (_, (_, bag :: EnumMap ItemId ItemQuant
bag)) = EnumMap ItemId ItemQuant -> Bool
desirableBagFloor EnumMap ItemId ItemQuant
bag
      focused :: Bool
focused = Skills -> Speed
gearSpeed Skills
actorMaxSk Speed -> Speed -> Bool
forall a. Ord a => a -> a -> Bool
< Speed
speedWalk Bool -> Bool -> Bool
|| Bool
condHpTooLow
      couldMoveLastTurn :: Bool
couldMoveLastTurn =  -- approximated; could have changed
        let actorSk :: Skills
actorSk = if Maybe ActorId
mleader Maybe ActorId -> Maybe ActorId -> Bool
forall a. Eq a => a -> a -> Bool
== ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
aid then Skills
actorMaxSk else Skills
actorMinSk
        in Skill -> Skills -> X
Ability.getSk Skill
Ability.SkMove Skills
actorSk X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 0
      isStuck :: Bool
isStuck = Actor -> Bool
actorWaits Actor
b Bool -> Bool -> Bool
&& Bool
couldMoveLastTurn
      slackTactic :: Bool
slackTactic =
        Player -> Tactic
ftactic (Faction -> Player
gplayer Faction
fact)
          Tactic -> [Tactic] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ Tactic
Ability.TMeleeAndRanged, Tactic
Ability.TMeleeAdjacent
                 , Tactic
Ability.TBlock, Tactic
Ability.TRoam, Tactic
Ability.TPatrol ]
      setPath :: Target -> m (Maybe TgtAndPath)
      setPath :: Target -> m (Maybe TgtAndPath)
setPath tgt :: Target
tgt = do
        let take7 :: TgtAndPath -> TgtAndPath
take7 tap :: TgtAndPath
tap@TgtAndPath{tapTgt :: TgtAndPath -> Target
tapTgt=TEnemy{}} =
              TgtAndPath
tap  -- @TEnemy@ needed for projecting, even by roaming actors
            take7 tap :: TgtAndPath
tap@TgtAndPath{tapPath :: TgtAndPath -> Maybe AndPath
tapPath=Just AndPath{..}} =
              -- Best path only followed 7 moves; then straight on. Cheaper.
              let path7 :: [Point]
path7 = X -> [Point] -> [Point]
forall a. X -> [a] -> [a]
take 7 [Point]
pathList
                  vOld :: Vector
vOld = Point -> Point -> Vector
towards (Actor -> Point
bpos Actor
b) Point
pathGoal
                  pNew :: Point
pNew = X -> X -> Point -> Vector -> Point
shiftBounded X
rXmax X
rYmax (Actor -> Point
bpos Actor
b) Vector
vOld
                  walkable :: Bool
walkable = TileSpeedup -> ContentId TileKind -> Bool
Tile.isWalkable TileSpeedup
coTileSpeedup (ContentId TileKind -> Bool) -> ContentId TileKind -> Bool
forall a b. (a -> b) -> a -> b
$ Level
lvl Level -> Point -> ContentId TileKind
`at` Point
pNew
                  tapTgt :: Target
tapTgt = Vector -> Target
TVector Vector
vOld
              in if Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Point
pathGoal  -- goal reached, so better know the tgt
                    Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
walkable  -- can't walk, so don't chase a vector
                 then TgtAndPath
tap
                 else $WTgtAndPath :: Target -> Maybe AndPath -> TgtAndPath
TgtAndPath{ Target
tapTgt :: Target
tapTgt :: Target
tapTgt
                                , tapPath :: Maybe AndPath
tapPath=AndPath -> Maybe AndPath
forall a. a -> Maybe a
Just $WAndPath :: Point -> [Point] -> Point -> X -> AndPath
AndPath{pathList :: [Point]
pathList=[Point]
path7, ..} }
            take7 tap :: TgtAndPath
tap = TgtAndPath
tap
        TgtAndPath
tgtpath <- ActorId -> Target -> m TgtAndPath
forall (m :: * -> *).
MonadClient m =>
ActorId -> Target -> m TgtAndPath
createPath ActorId
aid Target
tgt
        Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just (TgtAndPath -> Maybe TgtAndPath) -> TgtAndPath -> Maybe TgtAndPath
forall a b. (a -> b) -> a -> b
$ if Bool
slackTactic then TgtAndPath -> TgtAndPath
take7 TgtAndPath
tgtpath else TgtAndPath
tgtpath
      pickNewTarget :: m (Maybe TgtAndPath)
pickNewTarget = Maybe ActorId -> m (Maybe TgtAndPath)
pickNewTargetIgnore Maybe ActorId
forall a. Maybe a
Nothing
      pickNewTargetIgnore :: Maybe ActorId -> m (Maybe TgtAndPath)
      pickNewTargetIgnore :: Maybe ActorId -> m (Maybe TgtAndPath)
pickNewTargetIgnore maidToIgnore :: Maybe ActorId
maidToIgnore = do
        let f :: ActorId -> [(ActorId, Actor)]
f aidToIgnore :: ActorId
aidToIgnore = ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter ((ActorId -> ActorId -> Bool
forall a. Eq a => a -> a -> Bool
/= ActorId
aidToIgnore) (ActorId -> Bool)
-> ((ActorId, Actor) -> ActorId) -> (ActorId, Actor) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ActorId, Actor) -> ActorId
forall a b. (a, b) -> a
fst) [(ActorId, Actor)]
nearbyFoes
            notIgnoredFoes :: [(ActorId, Actor)]
notIgnoredFoes = [(ActorId, Actor)]
-> (ActorId -> [(ActorId, Actor)])
-> Maybe ActorId
-> [(ActorId, Actor)]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [(ActorId, Actor)]
nearbyFoes ActorId -> [(ActorId, Actor)]
f Maybe ActorId
maidToIgnore
        [(X, (ActorId, Actor))]
cfoes <- [(ActorId, Actor)] -> ActorId -> m [(X, (ActorId, Actor))]
forall (m :: * -> *).
MonadClient m =>
[(ActorId, Actor)] -> ActorId -> m [(X, (ActorId, Actor))]
closestFoes [(ActorId, Actor)]
notIgnoredFoes ActorId
aid
        case [(X, (ActorId, Actor))]
cfoes of
          (_, (aid2 :: ActorId
aid2, _)) : _ -> Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ ActorId -> Target
TEnemy ActorId
aid2
          [] | Bool
condInMelee -> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe TgtAndPath
forall a. Maybe a
Nothing  -- don't slow down fighters
            -- this looks a bit strange, because teammates stop in their tracks
            -- all around the map (unless very close to the combatant),
            -- but the intuition is, not being able to help immediately,
            -- and not being too friendly to each other, they just wait and see
            -- and also shout to the teammate to flee and lure foes into ambush
          [] -> do
            [(X, (Point, EnumMap ItemId ItemQuant))]
citemsRaw <- ActorId -> m [(X, (Point, EnumMap ItemId ItemQuant))]
forall (m :: * -> *).
MonadClient m =>
ActorId -> m [(X, (Point, EnumMap ItemId ItemQuant))]
closestItems ActorId
aid
            let citems :: Frequency (Point, EnumMap ItemId ItemQuant)
citems = Text
-> [(X, (Point, EnumMap ItemId ItemQuant))]
-> Frequency (Point, EnumMap ItemId ItemQuant)
forall a. Text -> [(X, a)] -> Frequency a
toFreq "closestItems"
                         ([(X, (Point, EnumMap ItemId ItemQuant))]
 -> Frequency (Point, EnumMap ItemId ItemQuant))
-> [(X, (Point, EnumMap ItemId ItemQuant))]
-> Frequency (Point, EnumMap ItemId ItemQuant)
forall a b. (a -> b) -> a -> b
$ ((X, (Point, EnumMap ItemId ItemQuant)) -> Bool)
-> [(X, (Point, EnumMap ItemId ItemQuant))]
-> [(X, (Point, EnumMap ItemId ItemQuant))]
forall a. (a -> Bool) -> [a] -> [a]
filter (X, (Point, EnumMap ItemId ItemQuant)) -> Bool
desirableFloor [(X, (Point, EnumMap ItemId ItemQuant))]
citemsRaw
            if Frequency (Point, EnumMap ItemId ItemQuant) -> Bool
forall a. Frequency a -> Bool
nullFreq Frequency (Point, EnumMap ItemId ItemQuant)
citems then do
              [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
ctriggersRaw <- FleeViaStairsOrEscape
-> ActorId -> m [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
forall (m :: * -> *).
MonadClient m =>
FleeViaStairsOrEscape
-> ActorId -> m [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
closestTriggers FleeViaStairsOrEscape
ViaAnything ActorId
aid
              let ctriggers :: Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers = Text
-> [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
-> Frequency (Point, (Point, EnumMap ItemId ItemQuant))
forall a. Text -> [(X, a)] -> Frequency a
toFreq "ctriggers" [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
ctriggersRaw
              if Frequency (Point, (Point, EnumMap ItemId ItemQuant)) -> Bool
forall a. Frequency a -> Bool
nullFreq Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers then do
                -- Tracking enemies is more important than exploring, but smell
                -- is unreliable and may lead to allies, not foes, so avoid it.
                [(X, (Point, Time))]
smpos <- if Bool
canSmell
                         then ActorId -> m [(X, (Point, Time))]
forall (m :: * -> *).
MonadClient m =>
ActorId -> m [(X, (Point, Time))]
closestSmell ActorId
aid
                         else [(X, (Point, Time))] -> m [(X, (Point, Time))]
forall (m :: * -> *) a. Monad m => a -> m a
return []
                case [(X, (Point, Time))]
smpos of
                  [] -> do
                    let vToTgt :: Vector -> m (Maybe TgtAndPath)
vToTgt v0 :: Vector
v0 = do
                          let vFreq :: Frequency Vector
vFreq = Text -> [(X, Vector)] -> Frequency Vector
forall a. Text -> [(X, a)] -> Frequency a
toFreq "vFreq"
                                      ([(X, Vector)] -> Frequency Vector)
-> [(X, Vector)] -> Frequency Vector
forall a b. (a -> b) -> a -> b
$ (20, Vector
v0) (X, Vector) -> [(X, Vector)] -> [(X, Vector)]
forall a. a -> [a] -> [a]
: (Vector -> (X, Vector)) -> [Vector] -> [(X, Vector)]
forall a b. (a -> b) -> [a] -> [b]
map (1,) [Vector]
moves
                          Vector
v <- Rnd Vector -> m Vector
forall (m :: * -> *) a. MonadClient m => Rnd a -> m a
rndToAction (Rnd Vector -> m Vector) -> Rnd Vector -> m Vector
forall a b. (a -> b) -> a -> b
$ Frequency Vector -> Rnd Vector
forall a. Show a => Frequency a -> Rnd a
frequency Frequency Vector
vFreq
                          -- Items and smells, etc. considered every 7 moves.
                          let pathSource :: Point
pathSource = Actor -> Point
bpos Actor
b
                              tra :: [Point]
tra = X -> X -> Point -> [Vector] -> [Point]
trajectoryToPathBounded
                                      X
rXmax X
rYmax Point
pathSource (X -> Vector -> [Vector]
forall a. X -> a -> [a]
replicate 7 Vector
v)
                              pathList :: [Point]
pathList = ([Point] -> Point) -> [[Point]] -> [Point]
forall a b. (a -> b) -> [a] -> [b]
map [Point] -> Point
forall a. [a] -> a
head ([[Point]] -> [Point]) -> [[Point]] -> [Point]
forall a b. (a -> b) -> a -> b
$ [Point] -> [[Point]]
forall a. Eq a => [a] -> [[a]]
group [Point]
tra
                              pathGoal :: Point
pathGoal = [Point] -> Point
forall a. [a] -> a
last [Point]
pathList
                              pathLen :: X
pathLen = [Point] -> X
forall a. [a] -> X
length [Point]
pathList
                          Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just (TgtAndPath -> Maybe TgtAndPath) -> TgtAndPath -> Maybe TgtAndPath
forall a b. (a -> b) -> a -> b
$
                            $WTgtAndPath :: Target -> Maybe AndPath -> TgtAndPath
TgtAndPath
                              { tapTgt :: Target
tapTgt = Vector -> Target
TVector Vector
v
                              , tapPath :: Maybe AndPath
tapPath = if X
pathLen X -> X -> Bool
forall a. Eq a => a -> a -> Bool
== 0
                                          then Maybe AndPath
forall a. Maybe a
Nothing
                                          else AndPath -> Maybe AndPath
forall a. a -> Maybe a
Just $WAndPath :: Point -> [Point] -> Point -> X -> AndPath
AndPath{..} }
                        oldpos :: Point
oldpos = Point -> Maybe Point -> Point
forall a. a -> Maybe a -> a
fromMaybe (Actor -> Point
bpos Actor
b) (Actor -> Maybe Point
boldpos Actor
b)
                        vOld :: Vector
vOld = Actor -> Point
bpos Actor
b Point -> Point -> Vector
`vectorToFrom` Point
oldpos
                        pNew :: Point
pNew = X -> X -> Point -> Vector -> Point
shiftBounded X
rXmax X
rYmax (Actor -> Point
bpos Actor
b) Vector
vOld
                    if Bool
slackTactic Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
isStuck
                       Bool -> Bool -> Bool
&& Vector -> Bool
isUnit Vector
vOld Bool -> Bool -> Bool
&& Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
/= Point
pNew
                            -- both are needed, e.g., when just teleported
                            -- or when the shift bounded by level borders
                       Bool -> Bool -> Bool
&& TileSpeedup -> ContentId TileKind -> Bool
Tile.isWalkable TileSpeedup
coTileSpeedup (Level
lvl Level -> Point -> ContentId TileKind
`at` Point
pNew)
                    then Vector -> m (Maybe TgtAndPath)
vToTgt Vector
vOld
                    else do
                      Maybe Point
upos <- ActorId -> m (Maybe Point)
forall (m :: * -> *). MonadClient m => ActorId -> m (Maybe Point)
closestUnknown ActorId
aid
                      case Maybe Point
upos of
                        Nothing -> do
                          -- If can't move (and so no BFS data), no info gained.
                          -- Or if can't alter and possibly stuck among rubble.
                          Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
canMove Bool -> Bool -> Bool
&& Bool
canAlter) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
                            (StateClient -> StateClient) -> m ()
forall (m :: * -> *).
MonadClient m =>
(StateClient -> StateClient) -> m ()
modifyClient ((StateClient -> StateClient) -> m ())
-> (StateClient -> StateClient) -> m ()
forall a b. (a -> b) -> a -> b
$ \cli :: StateClient
cli -> StateClient
cli {sexplored :: EnumSet LevelId
sexplored =
                              LevelId -> EnumSet LevelId -> EnumSet LevelId
forall k. Enum k => k -> EnumSet k -> EnumSet k
ES.insert (Actor -> LevelId
blid Actor
b) (StateClient -> EnumSet LevelId
sexplored StateClient
cli)}
                          [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
ctriggersRaw2 <- FleeViaStairsOrEscape
-> ActorId -> m [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
forall (m :: * -> *).
MonadClient m =>
FleeViaStairsOrEscape
-> ActorId -> m [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
closestTriggers FleeViaStairsOrEscape
ViaExit ActorId
aid
                          let ctriggers2 :: Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers2 = Text
-> [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
-> Frequency (Point, (Point, EnumMap ItemId ItemQuant))
forall a. Text -> [(X, a)] -> Frequency a
toFreq "ctriggers2" [(X, (Point, (Point, EnumMap ItemId ItemQuant)))]
ctriggersRaw2
                          if Frequency (Point, (Point, EnumMap ItemId ItemQuant)) -> Bool
forall a. Frequency a -> Bool
nullFreq Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers2 then do
                            [(X, (ActorId, Actor))]
afoes <- [(ActorId, Actor)] -> ActorId -> m [(X, (ActorId, Actor))]
forall (m :: * -> *).
MonadClient m =>
[(ActorId, Actor)] -> ActorId -> m [(X, (ActorId, Actor))]
closestFoes [(ActorId, Actor)]
allFoes ActorId
aid
                            case [(X, (ActorId, Actor))]
afoes of
                              (_, (aid2 :: ActorId
aid2, _)) : _ ->
                                -- All stones turned, time to win or die.
                                Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ ActorId -> Target
TEnemy ActorId
aid2
                              [] -> do
                                Point
furthest <- ActorId -> m Point
forall (m :: * -> *). MonadClient m => ActorId -> m Point
furthestKnown ActorId
aid
                                Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint TGoal
TKnown (Actor -> LevelId
blid Actor
b) Point
furthest
                          else do
                            (p :: Point
p, (p0 :: Point
p0, bag :: EnumMap ItemId ItemQuant
bag)) <- Rnd (Point, (Point, EnumMap ItemId ItemQuant))
-> m (Point, (Point, EnumMap ItemId ItemQuant))
forall (m :: * -> *) a. MonadClient m => Rnd a -> m a
rndToAction (Rnd (Point, (Point, EnumMap ItemId ItemQuant))
 -> m (Point, (Point, EnumMap ItemId ItemQuant)))
-> Rnd (Point, (Point, EnumMap ItemId ItemQuant))
-> m (Point, (Point, EnumMap ItemId ItemQuant))
forall a b. (a -> b) -> a -> b
$ Frequency (Point, (Point, EnumMap ItemId ItemQuant))
-> Rnd (Point, (Point, EnumMap ItemId ItemQuant))
forall a. Show a => Frequency a -> Rnd a
frequency Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers2
                            Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint (EnumMap ItemId ItemQuant -> Point -> TGoal
TEmbed EnumMap ItemId ItemQuant
bag Point
p0) (Actor -> LevelId
blid Actor
b) Point
p
                        Just p :: Point
p -> Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint TGoal
TUnknown (Actor -> LevelId
blid Actor
b) Point
p
                  (_, (p :: Point
p, _)) : _ -> Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint TGoal
TSmell (Actor -> LevelId
blid Actor
b) Point
p
              else do
                (p :: Point
p, (p0 :: Point
p0, bag :: EnumMap ItemId ItemQuant
bag)) <- Rnd (Point, (Point, EnumMap ItemId ItemQuant))
-> m (Point, (Point, EnumMap ItemId ItemQuant))
forall (m :: * -> *) a. MonadClient m => Rnd a -> m a
rndToAction (Rnd (Point, (Point, EnumMap ItemId ItemQuant))
 -> m (Point, (Point, EnumMap ItemId ItemQuant)))
-> Rnd (Point, (Point, EnumMap ItemId ItemQuant))
-> m (Point, (Point, EnumMap ItemId ItemQuant))
forall a b. (a -> b) -> a -> b
$ Frequency (Point, (Point, EnumMap ItemId ItemQuant))
-> Rnd (Point, (Point, EnumMap ItemId ItemQuant))
forall a. Show a => Frequency a -> Rnd a
frequency Frequency (Point, (Point, EnumMap ItemId ItemQuant))
ctriggers
                Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint (EnumMap ItemId ItemQuant -> Point -> TGoal
TEmbed EnumMap ItemId ItemQuant
bag Point
p0) (Actor -> LevelId
blid Actor
b) Point
p
            else do
              (p :: Point
p, bag :: EnumMap ItemId ItemQuant
bag) <- Rnd (Point, EnumMap ItemId ItemQuant)
-> m (Point, EnumMap ItemId ItemQuant)
forall (m :: * -> *) a. MonadClient m => Rnd a -> m a
rndToAction (Rnd (Point, EnumMap ItemId ItemQuant)
 -> m (Point, EnumMap ItemId ItemQuant))
-> Rnd (Point, EnumMap ItemId ItemQuant)
-> m (Point, EnumMap ItemId ItemQuant)
forall a b. (a -> b) -> a -> b
$ Frequency (Point, EnumMap ItemId ItemQuant)
-> Rnd (Point, EnumMap ItemId ItemQuant)
forall a. Show a => Frequency a -> Rnd a
frequency Frequency (Point, EnumMap ItemId ItemQuant)
citems
              Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint (EnumMap ItemId ItemQuant -> TGoal
TItem EnumMap ItemId ItemQuant
bag) (Actor -> LevelId
blid Actor
b) Point
p
      tellOthersNothingHere :: Point -> m (Maybe TgtAndPath)
tellOthersNothingHere pos :: Point
pos = do
        let f :: TgtAndPath -> Bool
f TgtAndPath{Target
tapTgt :: Target
tapTgt :: TgtAndPath -> Target
tapTgt} = case Target
tapTgt of
              TPoint _ lid :: LevelId
lid p :: Point
p -> Point
p Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
/= Point
pos Bool -> Bool -> Bool
|| LevelId
lid LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
/= Actor -> LevelId
blid Actor
b
              _ -> Bool
True
        (StateClient -> StateClient) -> m ()
forall (m :: * -> *).
MonadClient m =>
(StateClient -> StateClient) -> m ()
modifyClient ((StateClient -> StateClient) -> m ())
-> (StateClient -> StateClient) -> m ()
forall a b. (a -> b) -> a -> b
$ \cli :: StateClient
cli -> StateClient
cli {stargetD :: EnumMap ActorId TgtAndPath
stargetD = (TgtAndPath -> Bool)
-> EnumMap ActorId TgtAndPath -> EnumMap ActorId TgtAndPath
forall a k. (a -> Bool) -> EnumMap k a -> EnumMap k a
EM.filter TgtAndPath -> Bool
f (StateClient -> EnumMap ActorId TgtAndPath
stargetD StateClient
cli)}
        m (Maybe TgtAndPath)
pickNewTarget
      updateTgt :: TgtAndPath -> m (Maybe TgtAndPath)
      updateTgt :: TgtAndPath -> m (Maybe TgtAndPath)
updateTgt TgtAndPath{tapPath :: TgtAndPath -> Maybe AndPath
tapPath=Maybe AndPath
Nothing} = m (Maybe TgtAndPath)
pickNewTarget
      updateTgt tap :: TgtAndPath
tap@TgtAndPath{tapPath :: TgtAndPath -> Maybe AndPath
tapPath=Just AndPath{..},Target
tapTgt :: Target
tapTgt :: TgtAndPath -> Target
tapTgt} = case Target
tapTgt of
        TEnemy a :: ActorId
a -> do
          Actor
body <- (State -> Actor) -> m Actor
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Actor) -> m Actor) -> (State -> Actor) -> m Actor
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
a
          if | (Bool
condInMelee  -- fight close foes or nobody at all
                Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
focused Bool -> Bool -> Bool
&& Bool -> Bool
not ([(ActorId, Actor)] -> Bool
forall a. [a] -> Bool
null [(ActorId, Actor)]
nearbyFoes))  -- prefers closer foes
               Bool -> Bool -> Bool
&& ActorId
a ActorId -> [ActorId] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` ((ActorId, Actor) -> ActorId) -> [(ActorId, Actor)] -> [ActorId]
forall a b. (a -> b) -> [a] -> [b]
map (ActorId, Actor) -> ActorId
forall a b. (a, b) -> a
fst [(ActorId, Actor)]
nearbyFoes  -- old one not close enough
               Bool -> Bool -> Bool
|| Actor -> LevelId
blid Actor
body LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
/= Actor -> LevelId
blid Actor
b  -- wrong level
               Bool -> Bool -> Bool
|| Actor -> Bool
actorDying Actor
body -> -- foe already dying
               m (Maybe TgtAndPath)
pickNewTarget
             | ActorId -> EnumMap ActorId Point -> Bool
forall k a. Enum k => k -> EnumMap k a -> Bool
EM.member ActorId
aid EnumMap ActorId Point
fleeD -> m (Maybe TgtAndPath)
pickNewTarget
                 -- forget enemy positions to prevent attacking them again soon
             | Bool
otherwise -> do
               -- If there are no unwalkable tiles on the path to enemy,
               -- he gets target @TEnemy@ and then, even if such tiles emerge,
               -- the target updated by his moves remains @TEnemy@.
               -- Conversely, he is stuck with @TBlock@ if initial target had
               -- unwalkable tiles, for as long as they remain. Harmless quirk.
               Maybe AndPath
mpath <- ActorId -> Point -> m (Maybe AndPath)
forall (m :: * -> *).
MonadClient m =>
ActorId -> Point -> m (Maybe AndPath)
getCachePath ActorId
aid (Point -> m (Maybe AndPath)) -> Point -> m (Maybe AndPath)
forall a b. (a -> b) -> a -> b
$ Actor -> Point
bpos Actor
body
               case Maybe AndPath
mpath of
                 Nothing -> Maybe ActorId -> m (Maybe TgtAndPath)
pickNewTargetIgnore (ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
a)
                   -- enemy became unreachable
                 Just AndPath{pathList :: AndPath -> [Point]
pathList=[]} -> Maybe ActorId -> m (Maybe TgtAndPath)
pickNewTargetIgnore (ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
a)
                   -- he is his own enemy
                 Just AndPath{pathList :: AndPath -> [Point]
pathList= q :: Point
q : _} ->
                   -- If in melee and path blocked by actors (even proj.)
                   -- change target for this turn due to urgency.
                   -- Because of @condInMelee@ new target will be enemy,
                   -- if any other is left, or empty target.
                   -- If not in melee, keep target and consider your options
                   -- (wait until blocking actors move or displace or melee).
                   -- We don't want to wander away in search of loot, only to
                   -- turn around next turn when the enemy is again considered.
                   if Bool -> Bool
not Bool
condInMelee Bool -> Bool -> Bool
|| Bool -> Bool
not (Point -> Level -> Bool
occupiedBigLvl Point
q Level
lvl)
                                         Bool -> Bool -> Bool
&& Bool -> Bool
not (Point -> Level -> Bool
occupiedProjLvl Point
q Level
lvl)
                   then Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap{tapPath :: Maybe AndPath
tapPath=Maybe AndPath
mpath}
                   else Maybe ActorId -> m (Maybe TgtAndPath)
pickNewTargetIgnore (ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
a)
        -- In this case, need to retarget, to focus on foes that melee ours
        -- and not, e.g., on remembered foes or items.
        _ | Bool
condInMelee -> m (Maybe TgtAndPath)
pickNewTarget
        TPoint _ lid :: LevelId
lid _ | LevelId
lid LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
/= Actor -> LevelId
blid Actor
b -> m (Maybe TgtAndPath)
pickNewTarget  -- wrong level
        TPoint tgoal :: TGoal
tgoal lid :: LevelId
lid pos :: Point
pos -> case TGoal
tgoal of
          TEnemyPos _  -- chase last position even if foe hides
            | Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Point
pos -> Point -> m (Maybe TgtAndPath)
tellOthersNothingHere Point
pos
            | ActorId -> EnumMap ActorId Point -> Bool
forall k a. Enum k => k -> EnumMap k a -> Bool
EM.member ActorId
aid EnumMap ActorId Point
fleeD -> m (Maybe TgtAndPath)
pickNewTarget
                -- forget enemy positions to prevent attacking them again soon
            | Bool
otherwise -> do
              -- Here pick the closer enemy, remembered or seen, to avoid
              -- loops when approaching new enemy obscures him behind obstacle
              -- but reveals the previously remembered one, etc.
              let remainingDist :: X
remainingDist = Point -> Point -> X
chessDist (Actor -> Point
bpos Actor
b) Point
pos
              if ((ActorId, Actor) -> Bool) -> [(ActorId, Actor)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\(_, b3 :: Actor
b3) -> Point -> Point -> X
chessDist (Actor -> Point
bpos Actor
b) (Actor -> Point
bpos Actor
b3) X -> X -> Bool
forall a. Ord a => a -> a -> Bool
< X
remainingDist)
                     [(ActorId, Actor)]
nearbyFoes
              then m (Maybe TgtAndPath)
pickNewTarget
              else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          _ | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [(ActorId, Actor)] -> Bool
forall a. [a] -> Bool
null [(ActorId, Actor)]
nearbyFoes ->
            m (Maybe TgtAndPath)
pickNewTarget  -- prefer close foes to anything else
          -- Below we check the target could not be picked again in
          -- pickNewTarget (e.g., an item got picked up by our teammate)
          -- and only in this case it is invalidated.
          -- This ensures targets are eventually reached (unless a foe
          -- shows up) and not changed all the time mid-route
          -- to equally interesting, but perhaps a bit closer targets,
          -- most probably already targeted by other actors.
          TEmbed bag :: EnumMap ItemId ItemQuant
bag p :: Point
p -> Bool -> m (Maybe TgtAndPath) -> m (Maybe TgtAndPath)
forall a. (?callStack::CallStack) => Bool -> a -> a
assert (Point -> Point -> Bool
adjacent Point
pos Point
p) (m (Maybe TgtAndPath) -> m (Maybe TgtAndPath))
-> m (Maybe TgtAndPath) -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ do
            -- First, stairs and embedded items from @closestTriggers@.
            -- We don't check skills, because they normally don't change
            -- or we can put some equipment back and recover them.
            -- We don't determine if the stairs or embed are interesting
            -- (this changes with time), but allow the actor
            -- to reach them and then retarget. The two things we check
            -- is whether the embedded bag is still there, or used up
            -- and whether we happen to be already adjacent to @p@,
            -- even though not necessarily at @pos@.
            EnumMap ItemId ItemQuant
bag2 <- (State -> EnumMap ItemId ItemQuant) -> m (EnumMap ItemId ItemQuant)
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> EnumMap ItemId ItemQuant)
 -> m (EnumMap ItemId ItemQuant))
-> (State -> EnumMap ItemId ItemQuant)
-> m (EnumMap ItemId ItemQuant)
forall a b. (a -> b) -> a -> b
$ LevelId -> Point -> State -> EnumMap ItemId ItemQuant
getEmbedBag LevelId
lid Point
p  -- not @pos@
            if | EnumMap ItemId ItemQuant
bag EnumMap ItemId ItemQuant -> EnumMap ItemId ItemQuant -> Bool
forall a. Eq a => a -> a -> Bool
/= EnumMap ItemId ItemQuant
bag2 -> m (Maybe TgtAndPath)
pickNewTarget  -- others will notice soon enough
               | Point -> Point -> Bool
adjacent (Actor -> Point
bpos Actor
b) Point
p ->  -- regardless if at @pos@ or not
                   Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint TGoal
TKnown LevelId
lid (Actor -> Point
bpos Actor
b)
                     -- stay there one turn (high chance to become leader)
                     -- to enable triggering; if trigger fails
                     -- (e.g, changed skills), will retarget next turn (@TAny@)
               | Bool
otherwise -> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          TItem bag :: EnumMap ItemId ItemQuant
bag -> do
            EnumMap ItemId ItemQuant
bag2 <- (State -> EnumMap ItemId ItemQuant) -> m (EnumMap ItemId ItemQuant)
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> EnumMap ItemId ItemQuant)
 -> m (EnumMap ItemId ItemQuant))
-> (State -> EnumMap ItemId ItemQuant)
-> m (EnumMap ItemId ItemQuant)
forall a b. (a -> b) -> a -> b
$ LevelId -> Point -> State -> EnumMap ItemId ItemQuant
getFloorBag LevelId
lid Point
pos
            if | EnumMap ItemId ItemQuant
bag EnumMap ItemId ItemQuant -> EnumMap ItemId ItemQuant -> Bool
forall a. Eq a => a -> a -> Bool
/= EnumMap ItemId ItemQuant
bag2 -> m (Maybe TgtAndPath)
pickNewTarget  -- others will notice soon enough
               | Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Point
pos ->
                   Target -> m (Maybe TgtAndPath)
setPath (Target -> m (Maybe TgtAndPath)) -> Target -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TGoal -> LevelId -> Point -> Target
TPoint TGoal
TKnown LevelId
lid (Actor -> Point
bpos Actor
b)
                     -- stay there one turn (high chance to become leader)
                     -- to enable pickup; if pickup fails, will retarget
               | Bool
otherwise -> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          TSmell ->
            if Bool -> Bool
not Bool
canSmell
               Bool -> Bool -> Bool
|| let sml :: Time
sml = Time -> Point -> EnumMap Point Time -> Time
forall k a. Enum k => a -> k -> EnumMap k a -> a
EM.findWithDefault Time
timeZero Point
pos (Level -> EnumMap Point Time
lsmell Level
lvl)
                  in Time
sml Time -> Time -> Bool
forall a. Ord a => a -> a -> Bool
<= Level -> Time
ltime Level
lvl
            then m (Maybe TgtAndPath)
pickNewTarget  -- others will notice soon enough
            else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          TBlock -> do  -- e.g., door or first unknown tile of an area
            let t :: ContentId TileKind
t = Level
lvl Level -> Point -> ContentId TileKind
`at` Point
pos
            if Bool
isStuck  -- not a very important target, because blocked
               Bool -> Bool -> Bool
|| X
alterSkill X -> X -> Bool
forall a. Ord a => a -> a -> Bool
< Word8 -> X
forall a. Enum a => a -> X
fromEnum (Array Word8
lalter Array Word8 -> Point -> Word8
forall c. UnboxRepClass c => Array c -> Point -> c
PointArray.! Point
pos)
                    -- tile was searched or altered or skill lowered
               Bool -> Bool -> Bool
|| TileSpeedup -> ContentId TileKind -> Bool
Tile.isWalkable TileSpeedup
coTileSpeedup ContentId TileKind
t
                    -- tile is no longer unwalkable, so was explored
                    -- so time to recalculate target
            then m (Maybe TgtAndPath)
pickNewTarget  -- others will notice soon enough
            else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          TUnknown ->
            let t :: ContentId TileKind
t = Level
lvl Level -> Point -> ContentId TileKind
`at` Point
pos
            in if Level -> X
lexpl Level
lvl X -> X -> Bool
forall a. Ord a => a -> a -> Bool
<= Level -> X
lseen Level
lvl
                  Bool -> Bool -> Bool
|| Bool -> Bool
not (ContentId TileKind -> Bool
isUknownSpace ContentId TileKind
t)
               then m (Maybe TgtAndPath)
pickNewTarget  -- others will notice soon enough
               else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
          TKnown ->
            if Actor -> Point
bpos Actor
b Point -> Point -> Bool
forall a. Eq a => a -> a -> Bool
== Point
pos
               Bool -> Bool -> Bool
|| Bool
isStuck
               Bool -> Bool -> Bool
|| X
alterSkill X -> X -> Bool
forall a. Ord a => a -> a -> Bool
< Word8 -> X
forall a. Enum a => a -> X
fromEnum (Array Word8
lalter Array Word8 -> Point -> Word8
forall c. UnboxRepClass c => Array c -> Point -> c
PointArray.! Point
pos)
                    -- tile was searched or altered or skill lowered
            then m (Maybe TgtAndPath)
pickNewTarget  -- others unconcerned
            else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
        _ | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [(ActorId, Actor)] -> Bool
forall a. [a] -> Bool
null [(ActorId, Actor)]
nearbyFoes ->
          m (Maybe TgtAndPath)
pickNewTarget  -- prefer close foes to any vector
        TNonEnemy _ | Maybe ActorId
mleader Maybe ActorId -> Maybe ActorId -> Bool
forall a. Eq a => a -> a -> Bool
== ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
aid ->  -- a leader, never follow
          m (Maybe TgtAndPath)
pickNewTarget
        TNonEnemy a :: ActorId
a -> do
          Actor
body <- (State -> Actor) -> m Actor
forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
getsState ((State -> Actor) -> m Actor) -> (State -> Actor) -> m Actor
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
a
          if Actor -> LevelId
blid Actor
body LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
/= Actor -> LevelId
blid Actor
b  -- wrong level
          then m (Maybe TgtAndPath)
pickNewTarget
          else do
            -- Update path. If impossible, pick another target.
            Maybe AndPath
mpath <- ActorId -> Point -> m (Maybe AndPath)
forall (m :: * -> *).
MonadClient m =>
ActorId -> Point -> m (Maybe AndPath)
getCachePath ActorId
aid (Point -> m (Maybe AndPath)) -> Point -> m (Maybe AndPath)
forall a b. (a -> b) -> a -> b
$ Actor -> Point
bpos Actor
body
            case Maybe AndPath
mpath of
              Nothing -> m (Maybe TgtAndPath)
pickNewTarget
              Just AndPath{pathList :: AndPath -> [Point]
pathList=[]} -> m (Maybe TgtAndPath)
pickNewTarget
              _ -> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap{tapPath :: Maybe AndPath
tapPath=Maybe AndPath
mpath}
        TVector{} -> if X
pathLen X -> X -> Bool
forall a. Ord a => a -> a -> Bool
> 1
                     then Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe TgtAndPath -> m (Maybe TgtAndPath))
-> Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall a b. (a -> b) -> a -> b
$ TgtAndPath -> Maybe TgtAndPath
forall a. a -> Maybe a
Just TgtAndPath
tap
                     else m (Maybe TgtAndPath)
pickNewTarget
  if Bool
canMove
  then case Maybe TgtAndPath
oldTgtUpdatedPath of
    Nothing -> m (Maybe TgtAndPath)
pickNewTarget
    Just tap :: TgtAndPath
tap -> TgtAndPath -> m (Maybe TgtAndPath)
updateTgt TgtAndPath
tap
  else Maybe TgtAndPath -> m (Maybe TgtAndPath)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe TgtAndPath
forall a. Maybe a
Nothing