1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
|
-- | Possible causes of failure of request.
module Game.LambdaHack.Common.ReqFailure
( ReqFailure(..)
, impossibleReqFailure, showReqFailure
, permittedPrecious, permittedProject, permittedProjectAI, permittedApply
#ifdef EXPOSE_INTERNAL
-- * Internal operations
#endif
) where
import Prelude ()
import Game.LambdaHack.Core.Prelude
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.Content.RuleKind as RK
import qualified Game.LambdaHack.Definition.Ability as Ability
import Game.LambdaHack.Definition.Defs
-- | Possible causes of failure of request.
data ReqFailure =
MoveUnskilled
| MoveUnskilledAsleep
| MoveNothing
| MeleeUnskilled
| MeleeSelf
| MeleeDistant
| MeleeNotWeapon
| DisplaceUnskilled
| DisplaceDistant
| DisplaceAccess
| DisplaceMultiple
| DisplaceDying
| DisplaceBraced
| DisplaceImmobile
| DisplaceSupported
| AlterUnskilled
| AlterUnwalked
| AlterDistant
| AlterBlockActor
| AlterBlockItem
| AlterNothing
| CloseDistant
| CloseClosed
| CloseNothing
| CloseNonClosable
| WaitUnskilled
| YellUnskilled
| MoveItemUnskilled
| EqpOverfull
| EqpStackFull
| ApplyUnskilled
| ApplyFood
| ApplyRead
| ApplyPeriodic
| ApplyOutOfReach
| ApplyCharging
| ApplyNoEffects
| ItemNothing
| ItemNotCalm
| ItemOverStash
| NotCalmPrecious
| ProjectUnskilled
| ProjectAimOnself
| ProjectBlockTerrain
| ProjectBlockActor
| ProjectLobable
| ProjectOutOfReach
| ProjectFinderKeeper
| NoChangeDunLeader
deriving (Show, Eq)
impossibleReqFailure :: ReqFailure -> Bool
impossibleReqFailure reqFailure = case reqFailure of
MoveUnskilled -> False -- unidentified skill items
MoveUnskilledAsleep -> False -- unidentified skill items
MoveNothing -> True
MeleeUnskilled -> False -- unidentified skill items
MeleeSelf -> True
MeleeDistant -> True
MeleeNotWeapon -> False -- unidentified items
DisplaceUnskilled -> False -- unidentified skill items
DisplaceDistant -> True
DisplaceAccess -> True
DisplaceMultiple -> True
DisplaceDying -> True
DisplaceBraced -> True
DisplaceImmobile -> False -- unidentified skill items
DisplaceSupported -> False
AlterUnskilled -> False -- unidentified skill items
AlterUnwalked -> False
AlterDistant -> True
AlterBlockActor -> True -- adjacent actor always visible
AlterBlockItem -> True -- adjacent item always visible
AlterNothing -> True -- if tile known, its properties known
CloseDistant -> True
CloseClosed -> True
CloseNothing -> True
CloseNonClosable -> True
WaitUnskilled -> False -- unidentified skill items
YellUnskilled -> False -- unidentified skill items
MoveItemUnskilled -> False -- unidentified skill items
EqpOverfull -> True
EqpStackFull -> True
ApplyUnskilled -> False -- unidentified skill items
ApplyFood -> False -- unidentified skill items
ApplyRead -> False -- unidentified skill items
ApplyPeriodic -> False -- unidentified skill items
ApplyOutOfReach -> True
ApplyCharging -> False -- if aspect record unknown, charging unknown
ApplyNoEffects -> False -- if effects unknown, can't prevent it
ItemNothing -> True
ItemNotCalm -> False -- unidentified skill items
ItemOverStash -> True
NotCalmPrecious -> False -- unidentified skill items
ProjectUnskilled -> False -- unidentified skill items
ProjectAimOnself -> True
ProjectBlockTerrain -> True -- adjacent terrain always visible
ProjectBlockActor -> True -- adjacent actor always visible
ProjectLobable -> False -- unidentified skill items
ProjectOutOfReach -> True
ProjectFinderKeeper -> False
NoChangeDunLeader -> True
showReqFailure :: ReqFailure -> Text
showReqFailure reqFailure = case reqFailure of
MoveUnskilled -> "too low movement stat; use equipment menu to take off stat draining gear or switch to another teammate or wait until a stat draining condition passes as seen in organ menu"
MoveUnskilledAsleep -> "actor asleep; yawn to wake up"
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"
MeleeNotWeapon -> "trying to melee with not a weapon"
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 or supply stash"
AlterUnskilled -> "modify stat is needed to search or activate or transform terrain"
AlterUnwalked -> "too low modify stat to enter or activate or transform terrain; find and equip gear that improves the stat or try with a teammate whose skill menu shows a higher stat"
AlterDistant -> "trying to modify distant terrain"
AlterBlockActor -> "blocked by an actor"
AlterBlockItem -> "jammed by an item"
AlterNothing -> "wasting time on modifying nothing"
CloseDistant -> "trying to close a distant terrain"
CloseClosed -> "already closed"
CloseNothing -> "no adjacent terrain can be closed"
CloseNonClosable -> "cannot be closed"
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 triggering stat"
ApplyFood -> "trigger stat 1 is enough only to eat food from the ground and trigger simple appendages"
ApplyRead -> "activating cultural artifacts requires trigger stat 3"
ApplyPeriodic -> "manually activating periodic items requires trigger stat 4"
ApplyOutOfReach -> "cannot trigger an item out of reach"
ApplyCharging -> "cannot trigger an item that is still charging"
ApplyNoEffects -> "cannot trigger an item that produces no effect"
ItemNothing -> "wasting time on void item manipulation"
ItemNotCalm -> "you try to focus on your equipment but your calm fails you"
ItemOverStash -> "you roll in your hoard a little"
NotCalmPrecious -> "you are too distracted 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"
ProjectFinderKeeper -> "flinging any projectile you've found is out of the question; you prefer to keep them pristine and safe"
NoChangeDunLeader -> "no manual level change for your team"
-- The item should not be applied nor thrown because it's too delicate
-- to operate when not calm or because it's too precious to identify by use.
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
ItemDiscoMean itemAspectMean ->
IA.kmConst itemAspectMean || not isPrecious
-- Simplified, faster version, for inner AI loop.
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
ItemDiscoMean itemAspectMean ->
IA.kmConst itemAspectMean || 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 -> permittedPrecious forced calmE itemFull
-- Simplified, faster and more permissive version, for inner AI loop.
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 :: RK.RuleContent -> Time -> Int -> Bool -> Maybe CStore
-> ItemFull -> ItemQuant
-> Either ReqFailure Bool
permittedApply corule localTime skill calmE mstore
itemFull@ItemFull{itemKind, itemSuspect} kit =
if | skill < 1 -> Left ApplyUnskilled
| skill < 2
&& IK.isymbol itemKind /= IK.rsymbolNecklace (RK.ritemSymbols corule)
&& (IK.isymbol itemKind /= IK.rsymbolFood (RK.ritemSymbols corule)
|| mstore /= Just CGround) -> Left ApplyFood
| skill < 3
&& IK.isymbol itemKind == IK.rsymbolScroll (RK.ritemSymbols corule) ->
Left ApplyRead
| skill < 4
&& let arItem = aspectRecordFull itemFull
in IA.checkFlag Ability.Periodic arItem -> Left ApplyPeriodic
-- If the item is discharged, neither the kinetic hit nor
-- any effects activate, so there's no point triggering.
-- Note that if client doesn't know the timeout, here we may leak the fact
-- that the item is still charging, but the client risks destruction
-- if the item is, in fact, recharged and is not durable
-- (likely in case of jewellery). So it's OK (the message may be
-- somewhat alarming though).
| not $ hasCharge localTime kit -> Left ApplyCharging
| otherwise ->
if null (IK.ieffects itemKind)
&& (not itemSuspect || IA.isHumanTrinket itemKind)
then Left ApplyNoEffects
else permittedPrecious False calmE itemFull
|