-- | Operations on the 'Actor' type, and related, that need the 'State' type,
-- but not our custom monad types.
module Game.LambdaHack.Common.ActorState
  ( fidActorNotProjGlobalAssocs, actorAssocs, fidActorRegularAssocs
  , fidActorRegularIds, foeRegularAssocs, foeRegularList
  , friendRegularAssocs, friendRegularList, bagAssocs, bagAssocsK
  , posToBig, posToBigAssoc, posToProjs, posToProjAssocs
  , posToAids, posToAidAssocs, calculateTotal, itemPrice, findIid, combinedItems
  , getActorBody, getActorMaxSkills, actorCurrentSkills, canTraverse
  , getCarriedAssocsAndTrunk, getContainerBag
  , getFloorBag, getEmbedBag, getBodyStoreBag, getFactionStashBag
  , mapActorItems_, getActorAssocs
  , memActor, getLocalTime, regenCalmDelta, actorInAmbient
  , dispEnemy, itemToFull, fullAssocs, kitAssocs
  , getItemKindId, getIidKindId, getItemKind, getIidKind
  , getItemKindIdServer, getIidKindIdServer, getItemKindServer, getIidKindServer
  , tileAlterable, lidFromC, posFromC, anyFoeAdj, anyHarmfulFoeAdj
  , adjacentBigAssocs, adjacentProjAssocs, armorHurtBonus, inMelee
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import           Data.Int (Int64)
import           GHC.Exts (inline)

import           Game.LambdaHack.Common.Actor
import           Game.LambdaHack.Common.Faction
import           Game.LambdaHack.Common.Item
import           Game.LambdaHack.Common.Kind
import           Game.LambdaHack.Common.Level
import           Game.LambdaHack.Common.Misc
import           Game.LambdaHack.Common.Point
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 qualified Game.LambdaHack.Content.ItemKind as IK
import           Game.LambdaHack.Content.FactionKind
import qualified Game.LambdaHack.Content.TileKind as TK
import qualified Game.LambdaHack.Definition.Ability as Ability
import           Game.LambdaHack.Definition.Defs

fidActorNotProjGlobalAssocs :: FactionId -> State -> [(ActorId, Actor)]
fidActorNotProjGlobalAssocs :: FactionId -> State -> [(ActorId, Actor)]
fidActorNotProjGlobalAssocs FactionId
fid State
s =
  let f :: (ActorId, Actor) -> Bool
f (ActorId
_, Actor
b) = Bool -> Bool
not (Actor -> Bool
bproj Actor
b) Bool -> Bool -> Bool
&& Actor -> FactionId
bfid Actor
b FactionId -> FactionId -> Bool
forall a. Eq a => a -> a -> Bool
== FactionId
fid
  in ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter (ActorId, Actor) -> Bool
f ([(ActorId, Actor)] -> [(ActorId, Actor)])
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ EnumMap ActorId Actor -> [(ActorId, Actor)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap ActorId Actor -> [(ActorId, Actor)])
-> EnumMap ActorId Actor -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s

actorAssocs :: (FactionId -> Bool) -> LevelId -> State
            -> [(ActorId, Actor)]
actorAssocs :: (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorAssocs FactionId -> Bool
p LevelId
lid State
s =
  let f :: (ActorId, Actor) -> Bool
f (ActorId
_, Actor
b) = Actor -> LevelId
blid Actor
b LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
== LevelId
lid Bool -> Bool -> Bool
&& FactionId -> Bool
p (Actor -> FactionId
bfid Actor
b)
  in ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter (ActorId, Actor) -> Bool
f ([(ActorId, Actor)] -> [(ActorId, Actor)])
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ EnumMap ActorId Actor -> [(ActorId, Actor)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap ActorId Actor -> [(ActorId, Actor)])
-> EnumMap ActorId Actor -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s

actorRegularAssocs :: (FactionId -> Bool) -> LevelId -> State
                   -> [(ActorId, Actor)]
{-# INLINE actorRegularAssocs #-}
actorRegularAssocs :: (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs FactionId -> Bool
p LevelId
lid State
s =
  let f :: (ActorId, Actor) -> Bool
f (ActorId
_, Actor
b) = Bool -> Bool
not (Actor -> Bool
bproj Actor
b) Bool -> Bool -> Bool
&& Actor -> LevelId
blid Actor
b LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
== LevelId
lid Bool -> Bool -> Bool
&& FactionId -> Bool
p (Actor -> FactionId
bfid Actor
b) Bool -> Bool -> Bool
&& Actor -> Int64
bhp Actor
b Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0
  in ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter (ActorId, Actor) -> Bool
f ([(ActorId, Actor)] -> [(ActorId, Actor)])
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ EnumMap ActorId Actor -> [(ActorId, Actor)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap ActorId Actor -> [(ActorId, Actor)])
-> EnumMap ActorId Actor -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s

fidActorRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
fidActorRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
fidActorRegularAssocs FactionId
fid = (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs (FactionId -> FactionId -> Bool
forall a. Eq a => a -> a -> Bool
== FactionId
fid)

fidActorRegularIds :: FactionId -> LevelId -> State -> [ActorId]
fidActorRegularIds :: FactionId -> LevelId -> State -> [ActorId]
fidActorRegularIds FactionId
fid LevelId
lid State
s =
  ((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)] -> [ActorId])
-> [(ActorId, Actor)] -> [ActorId]
forall a b. (a -> b) -> a -> b
$ (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs (FactionId -> FactionId -> Bool
forall a. Eq a => a -> a -> Bool
== FactionId
fid) LevelId
lid State
s

foeRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
foeRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
foeRegularAssocs FactionId
fid LevelId
lid State
s =
  let fact :: Faction
fact = (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid) (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
  in (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs ((FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFoe FactionId
fid Faction
fact) LevelId
lid State
s

foeRegularList :: FactionId -> LevelId -> State -> [Actor]
foeRegularList :: FactionId -> LevelId -> State -> [Actor]
foeRegularList FactionId
fid LevelId
lid State
s =
  let fact :: Faction
fact = (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid) (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
  in ((ActorId, Actor) -> Actor) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> [a] -> [b]
map (ActorId, Actor) -> Actor
forall a b. (a, b) -> b
snd ([(ActorId, Actor)] -> [Actor]) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> a -> b
$ (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs ((FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFoe FactionId
fid Faction
fact) LevelId
lid State
s

friendRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
friendRegularAssocs :: FactionId -> LevelId -> State -> [(ActorId, Actor)]
friendRegularAssocs FactionId
fid LevelId
lid State
s =
  let fact :: Faction
fact = (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid) (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
  in (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs ((FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFriend FactionId
fid Faction
fact) LevelId
lid State
s

friendRegularList :: FactionId -> LevelId -> State -> [Actor]
friendRegularList :: FactionId -> LevelId -> State -> [Actor]
friendRegularList FactionId
fid LevelId
lid State
s =
  let fact :: Faction
fact = (EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid) (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
  in ((ActorId, Actor) -> Actor) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> [a] -> [b]
map (ActorId, Actor) -> Actor
forall a b. (a, b) -> b
snd ([(ActorId, Actor)] -> [Actor]) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> a -> b
$ (FactionId -> Bool) -> LevelId -> State -> [(ActorId, Actor)]
actorRegularAssocs ((FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFriend FactionId
fid Faction
fact) LevelId
lid State
s

bagAssocs :: State -> ItemBag -> [(ItemId, Item)]
bagAssocs :: State -> ItemBag -> [(ItemId, Item)]
bagAssocs State
s ItemBag
bag =
  let iidItem :: ItemId -> (ItemId, Item)
iidItem ItemId
iid = (ItemId
iid, ItemId -> State -> Item
getItemBody ItemId
iid State
s)
  in (ItemId -> (ItemId, Item)) -> [ItemId] -> [(ItemId, Item)]
forall a b. (a -> b) -> [a] -> [b]
map ItemId -> (ItemId, Item)
iidItem ([ItemId] -> [(ItemId, Item)]) -> [ItemId] -> [(ItemId, Item)]
forall a b. (a -> b) -> a -> b
$ ItemBag -> [ItemId]
forall k a. Enum k => EnumMap k a -> [k]
EM.keys ItemBag
bag

bagAssocsK :: State -> ItemBag -> [(ItemId, (Item, ItemQuant))]
bagAssocsK :: State -> ItemBag -> [(ItemId, (Item, ItemQuant))]
bagAssocsK State
s ItemBag
bag =
  let iidItem :: (ItemId, ItemQuant) -> (ItemId, (Item, ItemQuant))
iidItem (ItemId
iid, ItemQuant
kit) = (ItemId
iid, (ItemId -> State -> Item
getItemBody ItemId
iid State
s, ItemQuant
kit))
  in ((ItemId, ItemQuant) -> (ItemId, (Item, ItemQuant)))
-> [(ItemId, ItemQuant)] -> [(ItemId, (Item, ItemQuant))]
forall a b. (a -> b) -> [a] -> [b]
map (ItemId, ItemQuant) -> (ItemId, (Item, ItemQuant))
iidItem ([(ItemId, ItemQuant)] -> [(ItemId, (Item, ItemQuant))])
-> [(ItemId, ItemQuant)] -> [(ItemId, (Item, ItemQuant))]
forall a b. (a -> b) -> a -> b
$ ItemBag -> [(ItemId, ItemQuant)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs ItemBag
bag

posToBig :: Point -> LevelId -> State -> Maybe ActorId
posToBig :: Point -> LevelId -> State -> Maybe ActorId
posToBig Point
pos LevelId
lid State
s = Point -> Level -> Maybe ActorId
posToBigLvl Point
pos (Level -> Maybe ActorId) -> Level -> Maybe ActorId
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid

posToBigAssoc :: Point -> LevelId -> State -> Maybe (ActorId, Actor)
posToBigAssoc :: Point -> LevelId -> State -> Maybe (ActorId, Actor)
posToBigAssoc Point
pos LevelId
lid State
s =
  let maid :: Maybe ActorId
maid = Point -> Level -> Maybe ActorId
posToBigLvl Point
pos (Level -> Maybe ActorId) -> Level -> Maybe ActorId
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid
  in (ActorId -> (ActorId, Actor))
-> Maybe ActorId -> Maybe (ActorId, Actor)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\ActorId
aid -> (ActorId
aid, ActorId -> State -> Actor
getActorBody ActorId
aid State
s)) Maybe ActorId
maid

posToProjs :: Point -> LevelId -> State -> [ActorId]
posToProjs :: Point -> LevelId -> State -> [ActorId]
posToProjs Point
pos LevelId
lid State
s = Point -> Level -> [ActorId]
posToProjsLvl Point
pos (Level -> [ActorId]) -> Level -> [ActorId]
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid

posToProjAssocs :: Point -> LevelId -> State -> [(ActorId, Actor)]
posToProjAssocs :: Point -> LevelId -> State -> [(ActorId, Actor)]
posToProjAssocs Point
pos LevelId
lid State
s =
  let l :: [ActorId]
l = Point -> Level -> [ActorId]
posToProjsLvl Point
pos (Level -> [ActorId]) -> Level -> [ActorId]
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid
  in (ActorId -> (ActorId, Actor)) -> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> [a] -> [b]
map (\ActorId
aid -> (ActorId
aid, ActorId -> State -> Actor
getActorBody ActorId
aid State
s)) [ActorId]
l

posToAids :: Point -> LevelId -> State -> [ActorId]
posToAids :: Point -> LevelId -> State -> [ActorId]
posToAids Point
pos LevelId
lid State
s = Point -> Level -> [ActorId]
posToAidsLvl Point
pos (Level -> [ActorId]) -> Level -> [ActorId]
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid

posToAidAssocs :: Point -> LevelId -> State -> [(ActorId, Actor)]
posToAidAssocs :: Point -> LevelId -> State -> [(ActorId, Actor)]
posToAidAssocs Point
pos LevelId
lid State
s =
  let l :: [ActorId]
l = Point -> Level -> [ActorId]
posToAidsLvl Point
pos (Level -> [ActorId]) -> Level -> [ActorId]
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid
  in (ActorId -> (ActorId, Actor)) -> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> [a] -> [b]
map (\ActorId
aid -> (ActorId
aid, ActorId -> State -> Actor
getActorBody ActorId
aid State
s)) [ActorId]
l

-- | Calculate loot's worth for a given faction.
calculateTotal :: FactionId -> State -> (ItemBag, Int)
calculateTotal :: FactionId -> State -> (ItemBag, Int)
calculateTotal FactionId
fid State
s =
  let bag :: ItemBag
bag = FactionId -> State -> ItemBag
combinedItems FactionId
fid State
s
      items :: [(Item, Int)]
items = ((ItemId, ItemQuant) -> (Item, Int))
-> [(ItemId, ItemQuant)] -> [(Item, Int)]
forall a b. (a -> b) -> [a] -> [b]
map (\(ItemId
iid, (Int
k, ItemTimers
_)) -> (ItemId -> State -> Item
getItemBody ItemId
iid State
s, Int
k)) ([(ItemId, ItemQuant)] -> [(Item, Int)])
-> [(ItemId, ItemQuant)] -> [(Item, Int)]
forall a b. (a -> b) -> a -> b
$ ItemBag -> [(ItemId, ItemQuant)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs ItemBag
bag
      price :: (Item, Int) -> Int
price (Item
item, Int
k) = Int -> ItemKind -> Int
itemPrice Int
k (ItemKind -> Int) -> ItemKind -> Int
forall a b. (a -> b) -> a -> b
$ Item -> State -> ItemKind
getItemKind Item
item State
s
  in (ItemBag
bag, [Int] -> Int
forall a. Num a => [a] -> a
sum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ ((Item, Int) -> Int) -> [(Item, Int)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Item, Int) -> Int
price [(Item, Int)]
items)

-- | Price an item, taking count into consideration.
itemPrice :: Int -> IK.ItemKind -> Int
itemPrice :: Int -> ItemKind -> Int
itemPrice Int
jcount ItemKind
itemKind = case GroupName ItemKind -> [(GroupName ItemKind, Int)] -> Maybe Int
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup GroupName ItemKind
IK.VALUABLE ([(GroupName ItemKind, Int)] -> Maybe Int)
-> [(GroupName ItemKind, Int)] -> Maybe Int
forall a b. (a -> b) -> a -> b
$ ItemKind -> [(GroupName ItemKind, Int)]
IK.ifreq ItemKind
itemKind of
  Just Int
k -> Int
jcount Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
k
  Maybe Int
Nothing -> Int
0

findIid :: ActorId -> FactionId -> ItemId -> State
        -> [(ActorId, (Actor, CStore))]
findIid :: ActorId
-> FactionId -> ItemId -> State -> [(ActorId, (Actor, CStore))]
findIid ActorId
leader FactionId
fid ItemId
iid State
s =
  let actors :: [(ActorId, Actor)]
actors = FactionId -> State -> [(ActorId, Actor)]
fidActorNotProjGlobalAssocs FactionId
fid State
s
      itemsOfActor :: (ActorId, Actor) -> [(ItemId, (ActorId, (Actor, CStore)))]
itemsOfActor (ActorId
aid, Actor
b) =
        let itemsOfCStore :: CStore -> [(ItemId, (ActorId, (Actor, CStore)))]
itemsOfCStore CStore
store =
              let bag :: ItemBag
bag = Actor -> CStore -> State -> ItemBag
getBodyStoreBag Actor
b CStore
store State
s
              in (ItemId -> (ItemId, (ActorId, (Actor, CStore))))
-> [ItemId] -> [(ItemId, (ActorId, (Actor, CStore)))]
forall a b. (a -> b) -> [a] -> [b]
map (\ItemId
iid2 -> (ItemId
iid2, (ActorId
aid, (Actor
b, CStore
store)))) (ItemBag -> [ItemId]
forall k a. Enum k => EnumMap k a -> [k]
EM.keys ItemBag
bag)
            stores :: [CStore]
stores = [CStore
CEqp, CStore
COrgan] [CStore] -> [CStore] -> [CStore]
forall a. [a] -> [a] -> [a]
++ [CStore
CStash | ActorId
aid ActorId -> ActorId -> Bool
forall a. Eq a => a -> a -> Bool
== ActorId
leader]
        in (CStore -> [(ItemId, (ActorId, (Actor, CStore)))])
-> [CStore] -> [(ItemId, (ActorId, (Actor, CStore)))]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap CStore -> [(ItemId, (ActorId, (Actor, CStore)))]
itemsOfCStore [CStore]
stores
      items :: [(ItemId, (ActorId, (Actor, CStore)))]
items = ((ActorId, Actor) -> [(ItemId, (ActorId, (Actor, CStore)))])
-> [(ActorId, Actor)] -> [(ItemId, (ActorId, (Actor, CStore)))]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ActorId, Actor) -> [(ItemId, (ActorId, (Actor, CStore)))]
itemsOfActor [(ActorId, Actor)]
actors
  in ((ItemId, (ActorId, (Actor, CStore)))
 -> (ActorId, (Actor, CStore)))
-> [(ItemId, (ActorId, (Actor, CStore)))]
-> [(ActorId, (Actor, CStore))]
forall a b. (a -> b) -> [a] -> [b]
map (ItemId, (ActorId, (Actor, CStore))) -> (ActorId, (Actor, CStore))
forall a b. (a, b) -> b
snd ([(ItemId, (ActorId, (Actor, CStore)))]
 -> [(ActorId, (Actor, CStore))])
-> [(ItemId, (ActorId, (Actor, CStore)))]
-> [(ActorId, (Actor, CStore))]
forall a b. (a -> b) -> a -> b
$ ((ItemId, (ActorId, (Actor, CStore))) -> Bool)
-> [(ItemId, (ActorId, (Actor, CStore)))]
-> [(ItemId, (ActorId, (Actor, CStore)))]
forall a. (a -> Bool) -> [a] -> [a]
filter ((ItemId -> ItemId -> Bool
forall a. Eq a => a -> a -> Bool
== ItemId
iid) (ItemId -> Bool)
-> ((ItemId, (ActorId, (Actor, CStore))) -> ItemId)
-> (ItemId, (ActorId, (Actor, CStore)))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ItemId, (ActorId, (Actor, CStore))) -> ItemId
forall a b. (a, b) -> a
fst) [(ItemId, (ActorId, (Actor, CStore)))]
items

-- Trunk not considered (if stolen).
combinedItems :: FactionId -> State -> ItemBag
combinedItems :: FactionId -> State -> ItemBag
combinedItems FactionId
fid State
s =
  let stashBag :: ItemBag
stashBag = FactionId -> State -> ItemBag
getFactionStashBag FactionId
fid State
s
      bs :: [Actor]
bs = ((ActorId, Actor) -> Actor) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> [a] -> [b]
map (ActorId, Actor) -> Actor
forall a b. (a, b) -> b
snd ([(ActorId, Actor)] -> [Actor]) -> [(ActorId, Actor)] -> [Actor]
forall a b. (a -> b) -> a -> b
$ (FactionId -> State -> [(ActorId, Actor)])
-> FactionId -> State -> [(ActorId, Actor)]
forall a. a -> a
inline FactionId -> State -> [(ActorId, Actor)]
fidActorNotProjGlobalAssocs FactionId
fid State
s
  in (ItemQuant -> ItemQuant -> ItemQuant) -> [ItemBag] -> ItemBag
forall a k. (a -> a -> a) -> [EnumMap k a] -> EnumMap k a
EM.unionsWith ItemQuant -> ItemQuant -> ItemQuant
mergeItemQuant ([ItemBag] -> ItemBag) -> [ItemBag] -> ItemBag
forall a b. (a -> b) -> a -> b
$ (Actor -> ItemBag) -> [Actor] -> [ItemBag]
forall a b. (a -> b) -> [a] -> [b]
map Actor -> ItemBag
beqp [Actor]
bs [ItemBag] -> [ItemBag] -> [ItemBag]
forall a. [a] -> [a] -> [a]
++ [ItemBag
stashBag]

getActorBody :: ActorId -> State -> Actor
{-# INLINE getActorBody #-}
getActorBody :: ActorId -> State -> Actor
getActorBody ActorId
aid State
s = State -> EnumMap ActorId Actor
sactorD State
s EnumMap ActorId Actor -> ActorId -> Actor
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ActorId
aid

-- For now, faction and doctrine skill modifiers only change
-- the stats that affect permitted actions (@SkMove..SkApply@),
-- so the expensive @actorCurrentSkills@ operation doesn't need to be used
-- when checking the other skills, e.g., for FOV calculations,
-- and the @getActorMaxSkills@ cheap operation suffices.
-- (@ModeKind@ content is not currently validated in this respect.)
getActorMaxSkills :: ActorId -> State -> Ability.Skills
{-# INLINE getActorMaxSkills #-}
getActorMaxSkills :: ActorId -> State -> Skills
getActorMaxSkills ActorId
aid State
s = State -> ActorMaxSkills
sactorMaxSkills State
s ActorMaxSkills -> ActorId -> Skills
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ActorId
aid

actorCurrentSkills :: Maybe ActorId -> ActorId -> State -> Ability.Skills
actorCurrentSkills :: Maybe ActorId -> ActorId -> State -> Skills
actorCurrentSkills Maybe ActorId
mleader ActorId
aid State
s =
  let body :: Actor
body = ActorId -> State -> Actor
getActorBody ActorId
aid State
s
      actorMaxSk :: Skills
actorMaxSk = ActorId -> State -> Skills
getActorMaxSkills ActorId
aid State
s
      fact :: Faction
fact = (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
      skillsFromDoctrine :: Skills
skillsFromDoctrine = Doctrine -> Skills
Ability.doctrineSkills (Doctrine -> Skills) -> Doctrine -> Skills
forall a b. (a -> b) -> a -> b
$ Faction -> Doctrine
gdoctrine Faction
fact
      factionSkills :: Skills
factionSkills
        | ActorId -> Maybe ActorId
forall a. a -> Maybe a
Just ActorId
aid Maybe ActorId -> Maybe ActorId -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe ActorId
mleader = Skills
Ability.zeroSkills
        | Bool
otherwise = FactionKind -> Skills
fskillsOther (Faction -> FactionKind
gkind Faction
fact)
                      Skills -> Skills -> Skills
`Ability.addSkills` Skills
skillsFromDoctrine
  in Skills
actorMaxSk Skills -> Skills -> Skills
`Ability.addSkills` Skills
factionSkills

-- Check that the actor can move, also between levels and through doors.
-- Otherwise, it's too awkward for human player to control, e.g.,
-- being stuck in a room with revolving doors closing after one turn
-- and the player needing to micromanage opening such doors with
-- another actor all the time. Completely immovable actors
-- e.g., an impregnable surveillance camera in a crowded corridor,
-- are less of a problem due to micromanagment, but more due to
-- the constant disturbing of other actor's running, etc.
canTraverse :: ActorId -> State -> Bool
canTraverse :: ActorId -> State -> Bool
canTraverse ActorId
aid State
s =
  let actorMaxSk :: Skills
actorMaxSk = ActorId -> State -> Skills
getActorMaxSkills ActorId
aid State
s
  in Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMove Skills
actorMaxSk Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
     Bool -> Bool -> Bool
&& Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkAlter Skills
actorMaxSk Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8 -> Int
forall a. Enum a => a -> Int
fromEnum Word8
TK.talterForStairs

getCarriedAssocsAndTrunk :: Actor -> State -> [(ItemId, Item)]
getCarriedAssocsAndTrunk :: Actor -> State -> [(ItemId, Item)]
getCarriedAssocsAndTrunk Actor
b State
s =
  -- The trunk is important for a case of spotting a caught projectile
  -- with a stolen projecting item. This actually does happen.
  let trunk :: ItemBag
trunk = ItemId -> ItemQuant -> ItemBag
forall k a. Enum k => k -> a -> EnumMap k a
EM.singleton (Actor -> ItemId
btrunk Actor
b) ItemQuant
quantSingle
  in State -> ItemBag -> [(ItemId, Item)]
bagAssocs State
s (ItemBag -> [(ItemId, Item)]) -> ItemBag -> [(ItemId, Item)]
forall a b. (a -> b) -> a -> b
$ (ItemQuant -> ItemQuant -> ItemQuant) -> [ItemBag] -> ItemBag
forall a k. (a -> a -> a) -> [EnumMap k a] -> EnumMap k a
EM.unionsWith ItemQuant -> ItemQuant -> ItemQuant
forall a b. a -> b -> a
const [Actor -> ItemBag
beqp Actor
b, Actor -> ItemBag
borgan Actor
b, ItemBag
trunk]

getContainerBag :: Container -> State -> ItemBag
getContainerBag :: Container -> State -> ItemBag
getContainerBag Container
c State
s = case Container
c of
  CFloor LevelId
lid Point
p -> LevelId -> Point -> State -> ItemBag
getFloorBag LevelId
lid Point
p State
s
  CEmbed LevelId
lid Point
p -> LevelId -> Point -> State -> ItemBag
getEmbedBag LevelId
lid Point
p State
s
  CActor ActorId
aid CStore
cstore -> let b :: Actor
b = ActorId -> State -> Actor
getActorBody ActorId
aid State
s
                       in Actor -> CStore -> State -> ItemBag
getBodyStoreBag Actor
b CStore
cstore State
s
  CTrunk{} -> ItemBag
forall k a. EnumMap k a
EM.empty  -- for dummy/test/analytics cases

getFloorBag :: LevelId -> Point -> State -> ItemBag
getFloorBag :: LevelId -> Point -> State -> ItemBag
getFloorBag LevelId
lid Point
p State
s = ItemBag -> Point -> EnumMap Point ItemBag -> ItemBag
forall k a. Enum k => a -> k -> EnumMap k a -> a
EM.findWithDefault ItemBag
forall k a. EnumMap k a
EM.empty Point
p
                      (EnumMap Point ItemBag -> ItemBag)
-> EnumMap Point ItemBag -> ItemBag
forall a b. (a -> b) -> a -> b
$ Level -> EnumMap Point ItemBag
lfloor (State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid)

getEmbedBag :: LevelId -> Point -> State -> ItemBag
getEmbedBag :: LevelId -> Point -> State -> ItemBag
getEmbedBag LevelId
lid Point
p State
s = ItemBag -> Point -> EnumMap Point ItemBag -> ItemBag
forall k a. Enum k => a -> k -> EnumMap k a -> a
EM.findWithDefault ItemBag
forall k a. EnumMap k a
EM.empty Point
p
                      (EnumMap Point ItemBag -> ItemBag)
-> EnumMap Point ItemBag -> ItemBag
forall a b. (a -> b) -> a -> b
$ Level -> EnumMap Point ItemBag
lembed (State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid)

getBodyStoreBag :: Actor -> CStore -> State -> ItemBag
getBodyStoreBag :: Actor -> CStore -> State -> ItemBag
getBodyStoreBag Actor
b CStore
cstore State
s =
  case CStore
cstore of
    CStore
CGround -> LevelId -> Point -> State -> ItemBag
getFloorBag (Actor -> LevelId
blid Actor
b) (Actor -> Point
bpos Actor
b) State
s
    CStore
COrgan -> Actor -> ItemBag
borgan Actor
b
    CStore
CEqp -> Actor -> ItemBag
beqp Actor
b
    CStore
CStash -> FactionId -> State -> ItemBag
getFactionStashBag (Actor -> FactionId
bfid Actor
b) State
s

getFactionStashBag :: FactionId -> State -> ItemBag
getFactionStashBag :: FactionId -> State -> ItemBag
getFactionStashBag FactionId
fid State
s = case Faction -> Maybe (LevelId, Point)
gstash (Faction -> Maybe (LevelId, Point))
-> Faction -> Maybe (LevelId, Point)
forall a b. (a -> b) -> a -> b
$ State -> EnumMap FactionId Faction
sfactionD State
s EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid of
  Just (LevelId
lid, Point
pos) -> LevelId -> Point -> State -> ItemBag
getFloorBag LevelId
lid Point
pos State
s
  Maybe (LevelId, Point)
Nothing -> ItemBag
forall k a. EnumMap k a
EM.empty

mapActorItems_ :: Monad m
               => (CStore -> ItemId -> ItemQuant -> m ()) -> Actor -> State
               -> m ()
mapActorItems_ :: (CStore -> ItemId -> ItemQuant -> m ()) -> Actor -> State -> m ()
mapActorItems_ CStore -> ItemId -> ItemQuant -> m ()
f Actor
b State
s = do
  let notProcessed :: [CStore]
notProcessed = [CStore
CGround]
      sts :: [CStore]
sts = [CStore
forall a. Bounded a => a
minBound..CStore
forall a. Bounded a => a
maxBound] [CStore] -> [CStore] -> [CStore]
forall a. Eq a => [a] -> [a] -> [a]
\\ [CStore]
notProcessed
      g :: CStore -> m ()
g CStore
cstore = do
        let bag :: ItemBag
bag = Actor -> CStore -> State -> ItemBag
getBodyStoreBag Actor
b CStore
cstore State
s
        ((ItemId, ItemQuant) -> m ()) -> [(ItemId, ItemQuant)] -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
(a -> m ()) -> t a -> m ()
mapM_ ((ItemId -> ItemQuant -> m ()) -> (ItemId, ItemQuant) -> m ()
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((ItemId -> ItemQuant -> m ()) -> (ItemId, ItemQuant) -> m ())
-> (ItemId -> ItemQuant -> m ()) -> (ItemId, ItemQuant) -> m ()
forall a b. (a -> b) -> a -> b
$ CStore -> ItemId -> ItemQuant -> m ()
f CStore
cstore) ([(ItemId, ItemQuant)] -> m ()) -> [(ItemId, ItemQuant)] -> m ()
forall a b. (a -> b) -> a -> b
$ ItemBag -> [(ItemId, ItemQuant)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs ItemBag
bag
  (CStore -> m ()) -> [CStore] -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
(a -> m ()) -> t a -> m ()
mapM_ CStore -> m ()
g [CStore]
sts

getActorAssocs :: ActorId -> CStore -> State -> [(ItemId, (Item, ItemQuant))]
getActorAssocs :: ActorId -> CStore -> State -> [(ItemId, (Item, ItemQuant))]
getActorAssocs ActorId
aid CStore
cstore State
s =
  let b :: Actor
b = ActorId -> State -> Actor
getActorBody ActorId
aid State
s
  in State -> ItemBag -> [(ItemId, (Item, ItemQuant))]
bagAssocsK State
s (ItemBag -> [(ItemId, (Item, ItemQuant))])
-> ItemBag -> [(ItemId, (Item, ItemQuant))]
forall a b. (a -> b) -> a -> b
$ Actor -> CStore -> State -> ItemBag
getBodyStoreBag Actor
b CStore
cstore State
s

-- | Checks if the actor is present on the current level.
-- The order of argument here and in other functions is set to allow
--
-- > b <- getsState (memActor a)
memActor :: ActorId -> LevelId -> State -> Bool
memActor :: ActorId -> LevelId -> State -> Bool
memActor ActorId
aid LevelId
lid State
s =
  Bool -> (Actor -> Bool) -> Maybe Actor -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False ((LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
== LevelId
lid) (LevelId -> Bool) -> (Actor -> LevelId) -> Actor -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Actor -> LevelId
blid) (Maybe Actor -> Bool) -> Maybe Actor -> Bool
forall a b. (a -> b) -> a -> b
$ ActorId -> EnumMap ActorId Actor -> Maybe Actor
forall k a. Enum k => k -> EnumMap k a -> Maybe a
EM.lookup ActorId
aid (EnumMap ActorId Actor -> Maybe Actor)
-> EnumMap ActorId Actor -> Maybe Actor
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s

-- | Get current time from the dungeon data.
getLocalTime :: LevelId -> State -> Time
getLocalTime :: LevelId -> State -> Time
getLocalTime LevelId
lid State
s = Level -> Time
ltime (Level -> Time) -> Level -> Time
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid

regenCalmDelta :: ActorId -> Actor -> State -> Int64
regenCalmDelta :: ActorId -> Actor -> State -> Int64
regenCalmDelta ActorId
aid Actor
body State
s =
  let calmIncr :: Int64
calmIncr = Int64
oneM  -- normal rate of calm regen
      actorMaxSk :: Skills
actorMaxSk = ActorId -> State -> Skills
getActorMaxSkills ActorId
aid State
s
      maxDeltaCalm :: Int64
maxDeltaCalm = Int -> Int64
xM (Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMaxCalm Skills
actorMaxSk)
                     Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
- Actor -> Int64
bcalm Actor
body
      fact :: Faction
fact = (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
      -- Worry actor by non-projectile enemies felt (even if not seen)
      -- on the level within 3 steps. Even dying, but not hiding in wait.
      isHeardFoe :: (Point, ActorId) -> Bool
isHeardFoe (!Point
p, ActorId
aid2) =
        let b :: Actor
b = ActorId -> State -> Actor
getActorBody ActorId
aid2 State
s
        in (Point -> Point -> Int) -> Point -> Point -> Int
forall a. a -> a
inline Point -> Point -> Int
chessDist Point
p (Actor -> Point
bpos Actor
body) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
3
           Bool -> Bool -> Bool
&& Bool -> Bool
not (Actor -> Bool
actorWaitsOrSleeps Actor
b)  -- uncommon
           Bool -> Bool -> Bool
&& (FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFoe (Actor -> FactionId
bfid Actor
body) Faction
fact (Actor -> FactionId
bfid Actor
b)  -- costly
      actorRelaxed :: Bool
actorRelaxed = ResDelta -> Bool
deltaBenign (ResDelta -> Bool) -> ResDelta -> Bool
forall a b. (a -> b) -> a -> b
$ Actor -> ResDelta
bcalmDelta Actor
body
      actorWasRelaxed :: Bool
actorWasRelaxed = ResDelta -> Bool
deltaWasBenign (ResDelta -> Bool) -> ResDelta -> Bool
forall a b. (a -> b) -> a -> b
$ Actor -> ResDelta
bcalmDelta Actor
body
  in if | Bool -> Bool
not Bool
actorRelaxed -> Int64
0
            -- if no foes around, do not compensate and obscure distress,
            -- otherwise, don't increase delta further and suggest grave harm;
            -- note that in the effect, an actor that first hears distant
            -- action and then hears nearby enemy, won't notice the latter,
            -- which can be justified by distraction and is KISS and tactical
        | ((Point, ActorId) -> Bool) -> [(Point, ActorId)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Point, ActorId) -> Bool
isHeardFoe ([(Point, ActorId)] -> Bool) -> [(Point, ActorId)] -> Bool
forall a b. (a -> b) -> a -> b
$ EnumMap Point ActorId -> [(Point, ActorId)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap Point ActorId -> [(Point, ActorId)])
-> EnumMap Point ActorId -> [(Point, ActorId)]
forall a b. (a -> b) -> a -> b
$ Level -> EnumMap Point ActorId
lbig (Level -> EnumMap Point ActorId) -> Level -> EnumMap Point ActorId
forall a b. (a -> b) -> a -> b
$ State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
body ->
          Int64
minusM1  -- even if all calmness spent, keep informing the client;
                   -- from above we know delta won't get too large here
        | Bool
actorWasRelaxed -> Int64 -> Int64 -> Int64
forall a. Ord a => a -> a -> a
min Int64
calmIncr (Int64 -> Int64 -> Int64
forall a. Ord a => a -> a -> a
max Int64
0 Int64
maxDeltaCalm)
                                             -- if Calm is over max
        | Bool
otherwise -> Int64
0  -- don't regenerate if shortly after stress, to make
                          -- waking up actors via bad stealth easier

actorInAmbient :: Actor -> State -> Bool
actorInAmbient :: Actor -> State -> Bool
actorInAmbient Actor
b State
s =
  let lvl :: Level
lvl = (Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
b) (Dungeon -> Level) -> (State -> Dungeon) -> State -> Level
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> Dungeon
sdungeon (State -> Level) -> State -> Level
forall a b. (a -> b) -> a -> b
$ State
s
  in TileSpeedup -> ContentId TileKind -> Bool
Tile.isLit (COps -> TileSpeedup
coTileSpeedup (COps -> TileSpeedup) -> COps -> TileSpeedup
forall a b. (a -> b) -> a -> b
$ State -> COps
scops State
s) (Level
lvl Level -> Point -> ContentId TileKind
`at` Actor -> Point
bpos Actor
b)

-- Check whether an actor can displace another. We assume they are adjacent
-- and they are foes.
dispEnemy :: ActorId -> ActorId -> Ability.Skills -> State -> Bool
dispEnemy :: ActorId -> ActorId -> Skills -> State -> Bool
dispEnemy ActorId
source ActorId
target Skills
actorMaxSk State
s =
  let hasBackup :: Actor -> Bool
hasBackup Actor
b =
        let adjAssocs :: [(ActorId, Actor)]
adjAssocs = Actor -> State -> [(ActorId, Actor)]
adjacentBigAssocs Actor
b State
s
            fact :: Faction
fact = State -> EnumMap FactionId Faction
sfactionD State
s EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> FactionId
bfid Actor
b
            friend :: (ActorId, Actor) -> Bool
friend (ActorId
_, Actor
b2) = FactionId -> Faction -> FactionId -> Bool
isFriend (Actor -> FactionId
bfid Actor
b) Faction
fact (Actor -> FactionId
bfid Actor
b2) Bool -> Bool -> Bool
&& Actor -> Int64
bhp Actor
b2 Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0
        in ((ActorId, Actor) -> Bool) -> [(ActorId, Actor)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (ActorId, Actor) -> Bool
friend [(ActorId, Actor)]
adjAssocs
      sb :: Actor
sb = ActorId -> State -> Actor
getActorBody ActorId
source State
s
      tb :: Actor
tb = ActorId -> State -> Actor
getActorBody ActorId
target State
s
      tfact :: Faction
tfact = State -> EnumMap FactionId Faction
sfactionD State
s EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> FactionId
bfid Actor
tb
  in Actor -> Bool
bproj Actor
tb
     Bool -> Bool -> Bool
|| Bool -> Bool
not (Actor -> Bool
actorDying Actor
tb
             Bool -> Bool -> Bool
|| Actor -> Bool
actorWaits Actor
tb
             Bool -> Bool -> Bool
|| Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMove Skills
actorMaxSk Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0
                  -- sometimes this comes from sleep, but it's transient
                  -- and if we made exception for sleep, we would displace
                  -- immobile sleeping actors
             Bool -> Bool -> Bool
|| (LevelId, Point) -> Maybe (LevelId, Point)
forall a. a -> Maybe a
Just (Actor -> LevelId
blid Actor
tb, Actor -> Point
bpos Actor
tb) Maybe (LevelId, Point) -> Maybe (LevelId, Point) -> Bool
forall a. Eq a => a -> a -> Bool
== Faction -> Maybe (LevelId, Point)
gstash Faction
tfact
             Bool -> Bool -> Bool
|| Actor -> Bool
hasBackup Actor
sb Bool -> Bool -> Bool
&& Actor -> Bool
hasBackup Actor
tb)  -- solo actors are flexible

itemToFull :: ItemId -> State -> ItemFull
itemToFull :: ItemId -> State -> ItemFull
itemToFull ItemId
iid State
s =
  COps
-> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item -> ItemFull
itemToFull6 (State -> COps
scops State
s) (State -> DiscoveryKind
sdiscoKind State
s) (State -> DiscoveryAspect
sdiscoAspect State
s) ItemId
iid (ItemId -> State -> Item
getItemBody ItemId
iid State
s)

fullAssocs :: ActorId -> [CStore] -> State -> [(ItemId, ItemFull)]
fullAssocs :: ActorId -> [CStore] -> State -> [(ItemId, ItemFull)]
fullAssocs ActorId
aid [CStore]
cstores State
s =
  let allAssocs :: [(ItemId, (Item, ItemQuant))]
allAssocs = (CStore -> [(ItemId, (Item, ItemQuant))])
-> [CStore] -> [(ItemId, (Item, ItemQuant))]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\CStore
cstore -> ActorId -> CStore -> State -> [(ItemId, (Item, ItemQuant))]
getActorAssocs ActorId
aid CStore
cstore State
s) [CStore]
cstores
      iToFull :: (ItemId, (Item, ItemQuant)) -> (ItemId, ItemFull)
iToFull (ItemId
iid, (Item
item, ItemQuant
_kit)) =
        (ItemId
iid, COps
-> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item -> ItemFull
itemToFull6 (State -> COps
scops State
s) (State -> DiscoveryKind
sdiscoKind State
s) (State -> DiscoveryAspect
sdiscoAspect State
s) ItemId
iid Item
item)
  in ((ItemId, (Item, ItemQuant)) -> (ItemId, ItemFull))
-> [(ItemId, (Item, ItemQuant))] -> [(ItemId, ItemFull)]
forall a b. (a -> b) -> [a] -> [b]
map (ItemId, (Item, ItemQuant)) -> (ItemId, ItemFull)
iToFull [(ItemId, (Item, ItemQuant))]
allAssocs

kitAssocs :: ActorId -> [CStore] -> State -> [(ItemId, ItemFullKit)]
kitAssocs :: ActorId -> [CStore] -> State -> [(ItemId, ItemFullKit)]
kitAssocs ActorId
aid [CStore]
cstores State
s =
  let allAssocs :: [(ItemId, (Item, ItemQuant))]
allAssocs = (CStore -> [(ItemId, (Item, ItemQuant))])
-> [CStore] -> [(ItemId, (Item, ItemQuant))]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\CStore
cstore -> ActorId -> CStore -> State -> [(ItemId, (Item, ItemQuant))]
getActorAssocs ActorId
aid CStore
cstore State
s) [CStore]
cstores
      iToFull :: (ItemId, (Item, ItemQuant)) -> (ItemId, ItemFullKit)
iToFull (ItemId
iid, (Item
item, ItemQuant
kit)) =
        (ItemId
iid, ( COps
-> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item -> ItemFull
itemToFull6 (State -> COps
scops State
s) (State -> DiscoveryKind
sdiscoKind State
s) (State -> DiscoveryAspect
sdiscoAspect State
s) ItemId
iid Item
item
              , ItemQuant
kit ))
  in ((ItemId, (Item, ItemQuant)) -> (ItemId, ItemFullKit))
-> [(ItemId, (Item, ItemQuant))] -> [(ItemId, ItemFullKit)]
forall a b. (a -> b) -> [a] -> [b]
map (ItemId, (Item, ItemQuant)) -> (ItemId, ItemFullKit)
iToFull [(ItemId, (Item, ItemQuant))]
allAssocs

getItemKindId :: Item -> State -> ContentId IK.ItemKind
getItemKindId :: Item -> State -> ContentId ItemKind
getItemKindId Item
item State
s = case Item -> ItemIdentity
jkind Item
item of
  IdentityObvious ContentId ItemKind
ik -> ContentId ItemKind
ik
  IdentityCovered ItemKindIx
ix ContentId ItemKind
ik -> ContentId ItemKind
-> Maybe (ContentId ItemKind) -> ContentId ItemKind
forall a. a -> Maybe a -> a
fromMaybe ContentId ItemKind
ik (Maybe (ContentId ItemKind) -> ContentId ItemKind)
-> Maybe (ContentId ItemKind) -> ContentId ItemKind
forall a b. (a -> b) -> a -> b
$ ItemKindIx -> DiscoveryKind -> Maybe (ContentId ItemKind)
forall k a. Enum k => k -> EnumMap k a -> Maybe a
EM.lookup ItemKindIx
ix (DiscoveryKind -> Maybe (ContentId ItemKind))
-> DiscoveryKind -> Maybe (ContentId ItemKind)
forall a b. (a -> b) -> a -> b
$ State -> DiscoveryKind
sdiscoKind State
s

getIidKindId :: ItemId -> State -> ContentId IK.ItemKind
getIidKindId :: ItemId -> State -> ContentId ItemKind
getIidKindId ItemId
iid State
s = Item -> State -> ContentId ItemKind
getItemKindId (ItemId -> State -> Item
getItemBody ItemId
iid State
s) State
s

getItemKind :: Item -> State -> IK.ItemKind
getItemKind :: Item -> State -> ItemKind
getItemKind Item
item State
s = ContentData ItemKind -> ContentId ItemKind -> ItemKind
forall a. ContentData a -> ContentId a -> a
okind (COps -> ContentData ItemKind
coitem (COps -> ContentData ItemKind) -> COps -> ContentData ItemKind
forall a b. (a -> b) -> a -> b
$ State -> COps
scops State
s) (ContentId ItemKind -> ItemKind) -> ContentId ItemKind -> ItemKind
forall a b. (a -> b) -> a -> b
$ Item -> State -> ContentId ItemKind
getItemKindId Item
item State
s

getIidKind :: ItemId -> State -> IK.ItemKind
getIidKind :: ItemId -> State -> ItemKind
getIidKind ItemId
iid State
s = Item -> State -> ItemKind
getItemKind (ItemId -> State -> Item
getItemBody ItemId
iid State
s) State
s

getItemKindIdServer :: Item -> State -> ContentId IK.ItemKind
getItemKindIdServer :: Item -> State -> ContentId ItemKind
getItemKindIdServer Item
item State
s = case Item -> ItemIdentity
jkind Item
item of
  IdentityObvious ContentId ItemKind
ik -> ContentId ItemKind
ik
  IdentityCovered ItemKindIx
ix ContentId ItemKind
_ik -> ContentId ItemKind
-> Maybe (ContentId ItemKind) -> ContentId ItemKind
forall a. a -> Maybe a -> a
fromMaybe ([Char] -> ContentId ItemKind
forall a. HasCallStack => [Char] -> a
error ([Char] -> ContentId ItemKind) -> [Char] -> ContentId ItemKind
forall a b. (a -> b) -> a -> b
$ ItemIdentity -> [Char]
forall a. Show a => a -> [Char]
show (ItemIdentity -> [Char]) -> ItemIdentity -> [Char]
forall a b. (a -> b) -> a -> b
$ Item -> ItemIdentity
jkind Item
item)
                                      (ItemKindIx -> DiscoveryKind -> Maybe (ContentId ItemKind)
forall k a. Enum k => k -> EnumMap k a -> Maybe a
EM.lookup ItemKindIx
ix (DiscoveryKind -> Maybe (ContentId ItemKind))
-> DiscoveryKind -> Maybe (ContentId ItemKind)
forall a b. (a -> b) -> a -> b
$ State -> DiscoveryKind
sdiscoKind State
s)

getIidKindIdServer :: ItemId -> State -> ContentId IK.ItemKind
getIidKindIdServer :: ItemId -> State -> ContentId ItemKind
getIidKindIdServer ItemId
iid State
s = Item -> State -> ContentId ItemKind
getItemKindIdServer (ItemId -> State -> Item
getItemBody ItemId
iid State
s) State
s

getItemKindServer :: Item -> State -> IK.ItemKind
getItemKindServer :: Item -> State -> ItemKind
getItemKindServer Item
item State
s = ContentData ItemKind -> ContentId ItemKind -> ItemKind
forall a. ContentData a -> ContentId a -> a
okind (COps -> ContentData ItemKind
coitem (COps -> ContentData ItemKind) -> COps -> ContentData ItemKind
forall a b. (a -> b) -> a -> b
$ State -> COps
scops State
s) (ContentId ItemKind -> ItemKind) -> ContentId ItemKind -> ItemKind
forall a b. (a -> b) -> a -> b
$ Item -> State -> ContentId ItemKind
getItemKindIdServer Item
item State
s

getIidKindServer :: ItemId -> State -> IK.ItemKind
getIidKindServer :: ItemId -> State -> ItemKind
getIidKindServer ItemId
iid State
s = Item -> State -> ItemKind
getItemKindServer (ItemId -> State -> Item
getItemBody ItemId
iid State
s) State
s

tileAlterable :: LevelId -> Point -> State -> Bool
tileAlterable :: LevelId -> Point -> State -> Bool
tileAlterable LevelId
lid Point
pos State
s =
  let COps{TileSpeedup
coTileSpeedup :: TileSpeedup
coTileSpeedup :: COps -> TileSpeedup
coTileSpeedup} = State -> COps
scops State
s
      embeds :: ItemBag
embeds = LevelId -> Point -> State -> ItemBag
getEmbedBag LevelId
lid Point
pos State
s
      lvl :: Level
lvl = State -> Dungeon
sdungeon State
s Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! LevelId
lid
      t :: ContentId TileKind
t = Level
lvl Level -> Point -> ContentId TileKind
`at` Point
pos
      triggerable :: Bool
triggerable = (ItemId -> Bool) -> [ItemId] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\ItemId
iid -> Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Effect] -> Bool
forall a. [a] -> Bool
null ([Effect] -> Bool) -> [Effect] -> Bool
forall a b. (a -> b) -> a -> b
$ ItemKind -> [Effect]
IK.ieffects (ItemKind -> [Effect]) -> ItemKind -> [Effect]
forall a b. (a -> b) -> a -> b
$ ItemId -> State -> ItemKind
getIidKind ItemId
iid State
s)
                        (ItemBag -> [ItemId]
forall k a. Enum k => EnumMap k a -> [k]
EM.keys ItemBag
embeds)
  in TileSpeedup -> ContentId TileKind -> Bool
Tile.isModifiable TileSpeedup
coTileSpeedup ContentId TileKind
t Bool -> Bool -> Bool
|| Bool
triggerable

-- | Determine the dungeon level of the container. If the item is in
-- the shared stash, the level depends on which actor asks, not where
-- the stash is located physically.
lidFromC :: Container -> State -> LevelId
lidFromC :: Container -> State -> LevelId
lidFromC (CFloor LevelId
lid Point
_) State
_ = LevelId
lid
lidFromC (CEmbed LevelId
lid Point
_) State
_ = LevelId
lid
lidFromC (CActor ActorId
aid CStore
_) State
s = Actor -> LevelId
blid (Actor -> LevelId) -> Actor -> LevelId
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
aid State
s
lidFromC (CTrunk FactionId
_ LevelId
lid Point
_) State
_ = LevelId
lid

posFromC :: Container -> State -> Point
posFromC :: Container -> State -> Point
posFromC (CFloor LevelId
_ Point
pos) State
_ = Point
pos
posFromC (CEmbed LevelId
_ Point
pos) State
_ = Point
pos
posFromC (CActor ActorId
aid CStore
_) State
s = Actor -> Point
bpos (Actor -> Point) -> Actor -> Point
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
aid State
s
posFromC c :: Container
c@CTrunk{} State
_ = [Char] -> Point
forall a. HasCallStack => [Char] -> a
error ([Char] -> Point) -> [Char] -> Point
forall a b. (a -> b) -> a -> b
$ [Char]
"" [Char] -> Container -> [Char]
forall v. Show v => [Char] -> v -> [Char]
`showFailure` Container
c

vicinityFoeAdj :: ((ActorId, Actor) -> Bool) -> ActorId -> State -> Bool
{-# INLINE vicinityFoeAdj #-}
vicinityFoeAdj :: ((ActorId, Actor) -> Bool) -> ActorId -> State -> Bool
vicinityFoeAdj (ActorId, Actor) -> Bool
predicate ActorId
aid State
s =
  let body :: Actor
body = ActorId -> State -> Actor
getActorBody ActorId
aid State
s
      lvl :: Level
lvl = (Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
body) (Dungeon -> Level) -> (State -> Dungeon) -> State -> Level
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> Dungeon
sdungeon (State -> Level) -> State -> Level
forall a b. (a -> b) -> a -> b
$ State
s
      fact :: Faction
fact = (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 (State -> Faction) -> State -> Faction
forall a b. (a -> b) -> a -> b
$ State
s
      f :: Point -> Bool
f !Point
p = case Point -> Level -> Maybe ActorId
posToBigLvl Point
p Level
lvl of
        Maybe ActorId
Nothing -> Bool
False
        Just ActorId
aid2 -> let b2 :: Actor
b2 = ActorId -> State -> Actor
getActorBody ActorId
aid2 State
s
                     in FactionId -> Faction -> FactionId -> Bool
isFoe (Actor -> FactionId
bfid Actor
body) Faction
fact (Actor -> FactionId
bfid Actor
b2)
                        Bool -> Bool -> Bool
&& (ActorId, Actor) -> Bool
predicate (ActorId
aid2, Actor
b2)
      h :: Point -> Bool
h !Point
p = case Point -> Level -> [ActorId]
posToProjsLvl Point
p Level
lvl of
        [] -> Bool
False
        ActorId
aid2 : [ActorId]
_ -> FactionId -> Faction -> FactionId -> Bool
isFoe (Actor -> FactionId
bfid Actor
body) Faction
fact (FactionId -> Bool) -> (Actor -> FactionId) -> Actor -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Actor -> FactionId
bfid (Actor -> Bool) -> Actor -> Bool
forall a b. (a -> b) -> a -> b
$ ActorId -> State -> Actor
getActorBody ActorId
aid2 State
s
  in (Point -> Bool) -> [Point] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\Point
p -> Point -> Bool
f Point
p Bool -> Bool -> Bool
|| Point -> Bool
h Point
p) ([Point] -> Bool) -> [Point] -> Bool
forall a b. (a -> b) -> a -> b
$ Point -> [Point]
vicinityUnsafe (Point -> [Point]) -> Point -> [Point]
forall a b. (a -> b) -> a -> b
$ Actor -> Point
bpos Actor
body

-- | Require that any non-dying foe is adjacent. We include even
-- projectiles that explode when stricken down, because they can be caught
-- and then they don't explode, so it makes sense to focus on handling them.
-- If there are many projectiles in a single adjacent position, we only test
-- the first one, the one that would be hit in melee (this is not optimal
-- if the actor would need to flee instead of meleeing, but fleeing
-- with *many* projectiles adjacent is a possible waste of a move anyway).
anyFoeAdj :: ActorId -> State -> Bool
anyFoeAdj :: ActorId -> State -> Bool
anyFoeAdj = ((ActorId, Actor) -> Bool) -> ActorId -> State -> Bool
vicinityFoeAdj (Bool -> (ActorId, Actor) -> Bool
forall a b. a -> b -> a
const Bool
True)

anyHarmfulFoeAdj :: ActorMaxSkills -> ActorId -> State -> Bool
anyHarmfulFoeAdj :: ActorMaxSkills -> ActorId -> State -> Bool
anyHarmfulFoeAdj = ((ActorId, Actor) -> Bool) -> ActorId -> State -> Bool
vicinityFoeAdj (((ActorId, Actor) -> Bool) -> ActorId -> State -> Bool)
-> (ActorMaxSkills -> (ActorId, Actor) -> Bool)
-> ActorMaxSkills
-> ActorId
-> State
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ActorId -> Actor -> Bool) -> (ActorId, Actor) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((ActorId -> Actor -> Bool) -> (ActorId, Actor) -> Bool)
-> (ActorMaxSkills -> ActorId -> Actor -> Bool)
-> ActorMaxSkills
-> (ActorId, Actor)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ActorMaxSkills -> ActorId -> Actor -> Bool
actorWorthKilling

adjacentBigAssocs :: Actor -> State -> [(ActorId, Actor)]
{-# INLINE adjacentBigAssocs #-}
adjacentBigAssocs :: Actor -> State -> [(ActorId, Actor)]
adjacentBigAssocs Actor
body State
s =
  let lvl :: Level
lvl = (Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
body) (Dungeon -> Level) -> (State -> Dungeon) -> State -> Level
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> Dungeon
sdungeon (State -> Level) -> State -> Level
forall a b. (a -> b) -> a -> b
$ State
s
      f :: Point -> Maybe ActorId
f !Point
p = Point -> Level -> Maybe ActorId
posToBigLvl Point
p Level
lvl
      g :: ActorId -> (ActorId, Actor)
g !ActorId
aid = (ActorId
aid, ActorId -> State -> Actor
getActorBody ActorId
aid State
s)
  in (ActorId -> (ActorId, Actor)) -> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> [a] -> [b]
map ActorId -> (ActorId, Actor)
g ([ActorId] -> [(ActorId, Actor)])
-> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ (Point -> Maybe ActorId) -> [Point] -> [ActorId]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Point -> Maybe ActorId
f ([Point] -> [ActorId]) -> [Point] -> [ActorId]
forall a b. (a -> b) -> a -> b
$ Point -> [Point]
vicinityUnsafe (Point -> [Point]) -> Point -> [Point]
forall a b. (a -> b) -> a -> b
$ Actor -> Point
bpos Actor
body

adjacentProjAssocs :: Actor -> State -> [(ActorId, Actor)]
{-# INLINE adjacentProjAssocs #-}
adjacentProjAssocs :: Actor -> State -> [(ActorId, Actor)]
adjacentProjAssocs Actor
body State
s =
  let lvl :: Level
lvl = (Dungeon -> LevelId -> Level
forall k a. Enum k => EnumMap k a -> k -> a
EM.! Actor -> LevelId
blid Actor
body) (Dungeon -> Level) -> (State -> Dungeon) -> State -> Level
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> Dungeon
sdungeon (State -> Level) -> State -> Level
forall a b. (a -> b) -> a -> b
$ State
s
      f :: Point -> [ActorId]
f !Point
p = Point -> Level -> [ActorId]
posToProjsLvl Point
p Level
lvl
      g :: ActorId -> (ActorId, Actor)
g !ActorId
aid = (ActorId
aid, ActorId -> State -> Actor
getActorBody ActorId
aid State
s)
  in (ActorId -> (ActorId, Actor)) -> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> [a] -> [b]
map ActorId -> (ActorId, Actor)
g ([ActorId] -> [(ActorId, Actor)])
-> [ActorId] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ (Point -> [ActorId]) -> [Point] -> [ActorId]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Point -> [ActorId]
f ([Point] -> [ActorId]) -> [Point] -> [ActorId]
forall a b. (a -> b) -> a -> b
$ Point -> [Point]
vicinityUnsafe (Point -> [Point]) -> Point -> [Point]
forall a b. (a -> b) -> a -> b
$ Actor -> Point
bpos Actor
body

armorHurtBonus :: ActorId -> ActorId -> State -> Int
armorHurtBonus :: ActorId -> ActorId -> State -> Int
armorHurtBonus ActorId
source ActorId
target State
s =
  let sb :: Actor
sb = ActorId -> State -> Actor
getActorBody ActorId
source State
s
      sMaxSk :: Skills
sMaxSk = ActorId -> State -> Skills
getActorMaxSkills ActorId
source State
s
      tMaxSk :: Skills
tMaxSk = ActorId -> State -> Skills
getActorMaxSkills ActorId
target State
s
  in Bool -> Skills -> Skills -> Int
armorHurtCalculation (Actor -> Bool
bproj Actor
sb) Skills
sMaxSk Skills
tMaxSk

-- | Check if any non-dying foe is adjacent to any of our normal actors
-- and either can harm them via melee or can attack from a distance.
-- Otherwise no point meleeing him. Projectiles are ignored, because
-- they are not actively attempted to melee, see @meleeAny@.
-- This is regardless of whether our actor can melee or just needs to flee,
-- in which case alert is needed so that he is not slowed down by others.
-- However, if our actor can't move nor melee, no real combat is taking place.
-- This is needed only by AI and computed as lazily as possible.
inMelee :: ActorMaxSkills -> FactionId -> LevelId -> State -> Bool
inMelee :: ActorMaxSkills -> FactionId -> LevelId -> State -> Bool
inMelee !ActorMaxSkills
actorMaxSkills !FactionId
fid !LevelId
lid State
s =
  let fact :: Faction
fact = State -> EnumMap FactionId Faction
sfactionD State
s EnumMap FactionId Faction -> FactionId -> Faction
forall k a. Enum k => EnumMap k a -> k -> a
EM.! FactionId
fid
      f :: (ActorId, Actor) -> Bool
f (!ActorId
aid, !Actor
b) =
        Actor -> LevelId
blid Actor
b LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
== LevelId
lid
        Bool -> Bool -> Bool
&& Bool -> Bool
not (Actor -> Bool
bproj Actor
b)
        Bool -> Bool -> Bool
&& (FactionId -> Faction -> FactionId -> Bool)
-> FactionId -> Faction -> FactionId -> Bool
forall a. a -> a
inline FactionId -> Faction -> FactionId -> Bool
isFoe FactionId
fid Faction
fact (Actor -> FactionId
bfid Actor
b)  -- costly
        Bool -> Bool -> Bool
&& ActorMaxSkills -> ActorId -> Actor -> Bool
actorWorthKilling ActorMaxSkills
actorMaxSkills ActorId
aid Actor
b
      allFoes :: [(ActorId, Actor)]
allFoes = ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter (ActorId, Actor) -> Bool
f ([(ActorId, Actor)] -> [(ActorId, Actor)])
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ EnumMap ActorId Actor -> [(ActorId, Actor)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap ActorId Actor -> [(ActorId, Actor)])
-> EnumMap ActorId Actor -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s
      g :: (ActorId, Actor) -> Bool
g (!ActorId
aid, !Actor
b) = Actor -> FactionId
bfid Actor
b FactionId -> FactionId -> Bool
forall a. Eq a => a -> a -> Bool
== FactionId
fid
                     Bool -> Bool -> Bool
&& Actor -> LevelId
blid Actor
b LevelId -> LevelId -> Bool
forall a. Eq a => a -> a -> Bool
== LevelId
lid
                     Bool -> Bool -> Bool
&& Bool -> Bool
not (Actor -> Bool
bproj Actor
b)
                     Bool -> Bool -> Bool
&& Actor -> Int64
bhp Actor
b Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
> Int64
0
                     Bool -> Bool -> Bool
&& let actorMaxSk :: Skills
actorMaxSk = ActorMaxSkills
actorMaxSkills ActorMaxSkills -> ActorId -> Skills
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ActorId
aid
                        in Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMove Skills
actorMaxSk Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
                           Bool -> Bool -> Bool
|| ActorMaxSkills -> ActorId -> Actor -> Bool
actorCanMeleeToHarm ActorMaxSkills
actorMaxSkills ActorId
aid Actor
b
      allOurs :: [(ActorId, Actor)]
allOurs = ((ActorId, Actor) -> Bool)
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a. (a -> Bool) -> [a] -> [a]
filter (ActorId, Actor) -> Bool
g ([(ActorId, Actor)] -> [(ActorId, Actor)])
-> [(ActorId, Actor)] -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ EnumMap ActorId Actor -> [(ActorId, Actor)]
forall k a. Enum k => EnumMap k a -> [(k, a)]
EM.assocs (EnumMap ActorId Actor -> [(ActorId, Actor)])
-> EnumMap ActorId Actor -> [(ActorId, Actor)]
forall a b. (a -> b) -> a -> b
$ State -> EnumMap ActorId Actor
sactorD State
s
      -- We assume foes are less numerous, even though they may come
      -- from multiple factions and they contain projectiles,
      -- because we see all our actors, while many foes may be hidden.
      -- Consequently, we allocate the set of foe positions
      -- and avoid allocating ours, by iterating over our actors.
      -- This in O(mn) instead of O(m+n), but it allocates
      -- less and multiplicative constants are lower.
      -- We inspect adjacent locations of foe positions, not of ours,
      -- thus increasing allocation a bit, but not by much, because
      -- the set should be rather saturated.
      -- If there are no foes in sight, we don't iterate at all.
      setFoeVicinity :: EnumSet Point
setFoeVicinity =
        [Point] -> EnumSet Point
forall k. Enum k => [k] -> EnumSet k
ES.fromList ([Point] -> EnumSet Point) -> [Point] -> EnumSet Point
forall a b. (a -> b) -> a -> b
$ ((ActorId, Actor) -> [Point]) -> [(ActorId, Actor)] -> [Point]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Point -> [Point]
vicinityUnsafe (Point -> [Point])
-> ((ActorId, Actor) -> Point) -> (ActorId, Actor) -> [Point]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Actor -> Point
bpos (Actor -> Point)
-> ((ActorId, Actor) -> Actor) -> (ActorId, Actor) -> Point
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ActorId, Actor) -> Actor
forall a b. (a, b) -> b
snd) [(ActorId, Actor)]
allFoes
  in Bool -> Bool
not (EnumSet Point -> Bool
forall k. EnumSet k -> Bool
ES.null EnumSet Point
setFoeVicinity)  -- shortcut
     Bool -> Bool -> Bool
&& ((ActorId, Actor) -> Bool) -> [(ActorId, Actor)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\(ActorId
_, Actor
b) -> Actor -> Point
bpos Actor
b Point -> EnumSet Point -> Bool
forall k. Enum k => k -> EnumSet k -> Bool
`ES.member` EnumSet Point
setFoeVicinity) [(ActorId, Actor)]
allOurs