{-# LANGUAGE DeriveGeneric #-}
module Game.LambdaHack.Common.ReqFailure
( ReqFailure(..)
, impossibleReqFailure, showReqFailure
, permittedPrecious, permittedProject, permittedProjectAI, permittedApply
#ifdef EXPOSE_INTERNAL
#endif
) where
import Prelude ()
import Game.LambdaHack.Core.Prelude
import Data.Binary
import GHC.Generics (Generic)
import Game.LambdaHack.Common.Item
import qualified Game.LambdaHack.Common.ItemAspect as IA
import Game.LambdaHack.Common.Time
import qualified Game.LambdaHack.Content.ItemKind as IK
import qualified Game.LambdaHack.Definition.Ability as Ability
data ReqFailure =
MoveUnskilled
| MoveNothing
| MeleeUnskilled
| MeleeSelf
| MeleeDistant
| DisplaceUnskilled
| DisplaceDistant
| DisplaceAccess
| DisplaceMultiple
| DisplaceDying
| DisplaceBraced
| DisplaceImmobile
| DisplaceSupported
| AlterUnskilled
| AlterUnwalked
| AlterDistant
| AlterBlockActor
| AlterBlockItem
| AlterNothing
| WaitUnskilled
| YellUnskilled
| MoveItemUnskilled
| EqpOverfull
| EqpStackFull
| ApplyUnskilled
| ApplyFood
| ApplyRead
| ApplyPeriodic
| ApplyOutOfReach
| ApplyCharging
| ApplyNoEffects
| ItemNothing
| ItemNotCalm
| NotCalmPrecious
| ProjectUnskilled
| ProjectAimOnself
| ProjectBlockTerrain
| ProjectBlockActor
| ProjectLobable
| ProjectOutOfReach
| TriggerNothing
| NoChangeDunLeader
deriving (Show, Eq, Generic)
instance Binary ReqFailure
impossibleReqFailure :: ReqFailure -> Bool
impossibleReqFailure reqFailure = case reqFailure of
MoveUnskilled -> False
MoveNothing -> True
MeleeUnskilled -> False
MeleeSelf -> True
MeleeDistant -> True
DisplaceUnskilled -> False
DisplaceDistant -> True
DisplaceAccess -> True
DisplaceMultiple -> True
DisplaceDying -> True
DisplaceBraced -> True
DisplaceImmobile -> False
DisplaceSupported -> False
AlterUnskilled -> False
AlterUnwalked -> False
AlterDistant -> True
AlterBlockActor -> True
AlterBlockItem -> True
AlterNothing -> True
WaitUnskilled -> False
YellUnskilled -> False
MoveItemUnskilled -> False
EqpOverfull -> True
EqpStackFull -> True
ApplyUnskilled -> False
ApplyFood -> False
ApplyRead -> False
ApplyPeriodic -> False
ApplyOutOfReach -> True
ApplyCharging -> False
ApplyNoEffects -> False
ItemNothing -> True
ItemNotCalm -> False
NotCalmPrecious -> False
ProjectUnskilled -> False
ProjectAimOnself -> True
ProjectBlockTerrain -> True
ProjectBlockActor -> True
ProjectLobable -> False
ProjectOutOfReach -> True
TriggerNothing -> True
NoChangeDunLeader -> True
showReqFailure :: ReqFailure -> Text
showReqFailure reqFailure = case reqFailure of
MoveUnskilled -> "too low movement stat"
MoveNothing -> "wasting time on moving into obstacle"
MeleeUnskilled -> "too low melee combat stat"
MeleeSelf -> "trying to melee oneself"
MeleeDistant -> "trying to melee a distant foe"
DisplaceUnskilled -> "too low actor displacing stat"
DisplaceDistant -> "trying to displace a distant actor"
DisplaceAccess -> "trying to switch places without access"
DisplaceMultiple -> "trying to displace multiple actors"
DisplaceDying -> "trying to displace a dying foe"
DisplaceBraced -> "trying to displace a braced foe"
DisplaceImmobile -> "trying to displace an immobile foe"
DisplaceSupported -> "trying to displace a foe supported by teammates"
AlterUnskilled -> "alter stat is needed to search or exploit terrain"
AlterUnwalked -> "too low altering stat to enter or exploit terrain"
AlterDistant -> "trying to alter distant terrain"
AlterBlockActor -> "blocked by an actor"
AlterBlockItem -> "jammed by an item"
AlterNothing -> "wasting time on altering nothing"
WaitUnskilled -> "too low wait stat"
YellUnskilled -> "actors unskilled in waiting cannot yell/yawn"
MoveItemUnskilled -> "too low item moving stat"
EqpOverfull -> "cannot equip any more items"
EqpStackFull -> "cannot equip the whole item stack"
ApplyUnskilled -> "too low item applying stat"
ApplyFood -> "eating food requires apply stat 2"
ApplyRead -> "activating cultural artifacts requires apply stat 3"
ApplyPeriodic -> "manually activating periodic items requires apply stat 4"
ApplyOutOfReach -> "cannot apply an item out of reach"
ApplyCharging -> "cannot apply an item that is still charging"
ApplyNoEffects -> "cannot apply an item that produces no effect"
ItemNothing -> "wasting time on void item manipulation"
ItemNotCalm -> "you are too alarmed to use the shared stash"
NotCalmPrecious -> "you are too alarmed to handle such an exquisite item"
ProjectUnskilled -> "too low item flinging stat"
ProjectAimOnself -> "cannot aim at oneself"
ProjectBlockTerrain -> "aiming obstructed by terrain"
ProjectBlockActor -> "aiming blocked by an actor"
ProjectLobable -> "flinging a lobable item that stops at target position requires fling stat 3"
ProjectOutOfReach -> "cannot aim an item out of reach"
TriggerNothing -> "wasting time on triggering nothing"
NoChangeDunLeader -> "no manual level change for your team"
permittedPrecious :: Bool -> Bool -> ItemFull -> Either ReqFailure Bool
permittedPrecious forced calmE itemFull@ItemFull{itemDisco} =
let arItem = aspectRecordFull itemFull
isPrecious = IA.checkFlag Ability.Precious arItem
in if not forced && not calmE && isPrecious
then Left NotCalmPrecious
else Right $ IA.checkFlag Ability.Durable arItem
|| case itemDisco of
ItemDiscoFull{} -> True
_ -> not isPrecious
permittedPreciousAI :: Bool -> ItemFull -> Bool
permittedPreciousAI calmE itemFull@ItemFull{itemDisco} =
let arItem = aspectRecordFull itemFull
isPrecious = IA.checkFlag Ability.Precious arItem
in (calmE || not isPrecious)
&& IA.checkFlag Ability.Durable arItem
|| case itemDisco of
ItemDiscoFull{} -> True
_ -> not isPrecious
permittedProject :: Bool -> Int -> Bool -> ItemFull -> Either ReqFailure Bool
permittedProject forced skill calmE itemFull =
let arItem = aspectRecordFull itemFull
in if | not forced && skill < 1 -> Left ProjectUnskilled
| not forced
&& IA.checkFlag Ability.Lobable arItem
&& skill < 3 -> Left ProjectLobable
| otherwise -> case permittedPrecious forced calmE itemFull of
Left failure -> Left failure
Right False -> Right False
Right True -> Right $
let badSlot = case IA.aEqpSlot arItem of
Just Ability.EqpSlotShine -> False
Just _ -> True
Nothing -> IA.goesIntoEqp arItem
in not badSlot
permittedProjectAI :: Int -> Bool -> ItemFull -> Bool
permittedProjectAI skill calmE itemFull =
let arItem = aspectRecordFull itemFull
in if | skill < 1 -> False
| IA.checkFlag Ability.Lobable arItem
&& skill < 3 -> False
| otherwise -> permittedPreciousAI calmE itemFull
permittedApply :: Time -> Int -> Bool-> ItemFull -> ItemQuant
-> Either ReqFailure Bool
permittedApply localTime skill calmE
itemFull@ItemFull{itemKind, itemSuspect} kit =
if | skill < 1 -> Left ApplyUnskilled
| skill < 2 && IK.isymbol itemKind `notElem` [',', '"'] -> Left ApplyFood
| skill < 3 && IK.isymbol itemKind == '?' -> Left ApplyRead
| skill < 4
&& let arItem = aspectRecordFull itemFull
in IA.checkFlag Ability.Periodic arItem -> Left ApplyPeriodic
| not $ hasCharge localTime itemFull kit -> Left ApplyCharging
| otherwise ->
if null (IK.ieffects itemKind) && not itemSuspect
then Left ApplyNoEffects
else permittedPrecious False calmE itemFull