{-# LANGUAGE DeriveGeneric #-}
module Game.LambdaHack.Common.Actor
(
Actor(..), ResDelta(..), ActorMaxSkills, Watchfulness(..)
, deltasSerious, deltasHears, deltaBenign, deltaWasBenign, actorCanMelee
, gearSpeed, actorTemplate, actorWaits, actorWaitsOrSleeps, actorDying
, hpTooLow, calmEnough, hpEnough, hpFull, canSleep, prefersSleep
, checkAdjacent, eqpOverfull, eqpFreeN
, ActorDict, monsterGenChance, smellTimeout
) where
import Prelude ()
import Game.LambdaHack.Core.Prelude
import Data.Binary
import qualified Data.EnumMap.Strict as EM
import Data.Int (Int64)
import Data.Ratio
import GHC.Generics (Generic)
import qualified Game.LambdaHack.Definition.Ability as Ability
import qualified Game.LambdaHack.Core.Dice as Dice
import Game.LambdaHack.Common.Item
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Common.Point
import Game.LambdaHack.Core.Random
import Game.LambdaHack.Common.Time
import Game.LambdaHack.Common.Types
import Game.LambdaHack.Common.Vector
data Actor = Actor
{
btrunk :: ItemId
, bhp :: Int64
, bhpDelta :: ResDelta
, bcalm :: Int64
, bcalmDelta :: ResDelta
, bpos :: Point
, boldpos :: Maybe Point
, blid :: LevelId
, bfid :: FactionId
, btrajectory :: Maybe ([Vector], Speed)
, borgan :: ItemBag
, beqp :: ItemBag
, binv :: ItemBag
, bweapon :: Int
, bwatch :: Watchfulness
, bproj :: Bool
}
deriving (Show, Eq, Generic)
instance Binary Actor
data ResDelta = ResDelta
{ resCurrentTurn :: (Int64, Int64)
, resPreviousTurn :: (Int64, Int64)
}
deriving (Show, Eq, Generic)
instance Binary ResDelta
type ActorMaxSkills = EM.EnumMap ActorId Ability.Skills
type ActorDict = EM.EnumMap ActorId Actor
data Watchfulness = WWatch | WWait Int | WSleep | WWake
deriving (Show, Eq, Generic)
instance Binary Watchfulness
deltasSerious :: ResDelta -> Bool
deltasSerious ResDelta{..} = fst resCurrentTurn <= minusM2
|| fst resPreviousTurn <= minusM2
deltasHears :: ResDelta -> Bool
deltasHears ResDelta{..} = fst resCurrentTurn == minusM1
|| fst resPreviousTurn == minusM1
deltaBenign :: ResDelta -> Bool
deltaBenign ResDelta{resCurrentTurn} =
fst resCurrentTurn >= 0
deltaWasBenign :: ResDelta -> Bool
deltaWasBenign ResDelta{resPreviousTurn} =
fst resPreviousTurn >= 0
actorCanMelee :: ActorMaxSkills -> ActorId -> Actor -> Bool
actorCanMelee actorMaxSkills aid b =
let actorMaxSk = actorMaxSkills EM.! aid
condUsableWeapon = bweapon b > 0
canMelee = Ability.getSk Ability.SkMelee actorMaxSk > 0
in condUsableWeapon && canMelee
gearSpeed :: Ability.Skills -> Speed
gearSpeed actorMaxSk = toSpeed $
max minSpeed (Ability.getSk Ability.SkSpeed actorMaxSk)
actorTemplate :: ItemId -> Int64 -> Int64 -> Point -> LevelId -> FactionId
-> Bool
-> Actor
actorTemplate btrunk bhp bcalm bpos blid bfid bproj =
let btrajectory = Nothing
boldpos = Nothing
borgan = EM.empty
beqp = EM.empty
binv = EM.empty
bweapon = 0
bwatch = WWatch
bhpDelta = ResDelta (0, 0) (0, 0)
bcalmDelta = ResDelta (0, 0) (0, 0)
in Actor{..}
actorWaits :: Actor -> Bool
{-# INLINE actorWaits #-}
actorWaits b = case bwatch b of
WWait{} -> True
_ -> False
actorWaitsOrSleeps :: Actor -> Bool
{-# INLINE actorWaitsOrSleeps #-}
actorWaitsOrSleeps b = case bwatch b of
WWait{} -> True
WSleep -> True
_ -> False
actorDying :: Actor -> Bool
actorDying b = bhp b <= 0
|| bproj b && maybe True (null . fst) (btrajectory b)
hpTooLow :: Actor -> Ability.Skills -> Bool
hpTooLow b actorMaxSk =
5 * bhp b < xM (Ability.getSk Ability.SkMaxHP actorMaxSk) && bhp b <= xM 40
|| bhp b <= oneM
calmEnough :: Actor -> Ability.Skills -> Bool
calmEnough b actorMaxSk =
let calmMax = max 1 $ Ability.getSk Ability.SkMaxCalm actorMaxSk
in 2 * xM calmMax <= 3 * bcalm b && bcalm b > xM 10
hpEnough :: Actor -> Ability.Skills -> Bool
hpEnough b actorMaxSk =
xM (Ability.getSk Ability.SkMaxHP actorMaxSk) <= 2 * bhp b && bhp b > oneM
hpFull :: Actor -> Ability.Skills -> Bool
hpFull b actorMaxSk = xM (Ability.getSk Ability.SkMaxHP actorMaxSk) <= bhp b
canSleep :: Ability.Skills -> Bool
canSleep actorMaxSk = Ability.getSk Ability.SkWait actorMaxSk >= 3
&& (Ability.getSk Ability.SkSight actorMaxSk > 0
|| Ability.getSk Ability.SkHearing actorMaxSk > 0)
&& Ability.getSk Ability.SkAggression actorMaxSk < 2
prefersSleep :: Ability.Skills -> Bool
prefersSleep actorMaxSk = Ability.getSk Ability.SkMoveItem actorMaxSk <= 0
checkAdjacent :: Actor -> Actor -> Bool
checkAdjacent sb tb = blid sb == blid tb && adjacent (bpos sb) (bpos tb)
eqpOverfull :: Actor -> Int -> Bool
eqpOverfull b n = let size = sum $ map fst $ EM.elems $ beqp b
in assert (size <= 10 `blame` (b, n, size))
$ size + n > 10
eqpFreeN :: Actor -> Int
eqpFreeN b = let size = sum $ map fst $ EM.elems $ beqp b
in assert (size <= 10 `blame` (b, size))
$ 10 - size
monsterGenChance :: Dice.AbsDepth -> Dice.AbsDepth -> Int -> Int -> Rnd Bool
monsterGenChance (Dice.AbsDepth ldepth) (Dice.AbsDepth totalDepth)
lvlSpawned actorCoeff =
assert (totalDepth > 0 && ldepth > 0) $
let scaledDepth = ldepth * 10 `div` totalDepth
maxCoeff = 100 * 30
coeff = min maxCoeff $ actorCoeff * (lvlSpawned - scaledDepth - 2)
in chance $ 3%fromIntegral (coeff `max` 1)
smellTimeout :: Delta Time
smellTimeout = timeDeltaScale (Delta timeTurn) 200