{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, TupleSections #-}
-- | Weapons, treasure and all the other items in the game.
module Game.LambdaHack.Common.Item
  ( Item(..), ItemIdentity(..)
  , ItemKindIx, ItemDisco(..), ItemFull(..), ItemFullKit
  , DiscoveryKind, DiscoveryAspect, ItemIxMap, Benefit(..), DiscoveryBenefit
  , ItemTimer, ItemTimers, ItemQuant, ItemBag, ItemDict
  , toItemKindIx, quantSingle, itemToFull6, aspectRecordFull, strongestSlot
  , itemTimerZero, createItemTimer, shiftItemTimer
  , deltaOfItemTimer, charging, ncharges, hasCharge
  , strongestMelee, unknownMeleeBonus, unknownSpeedBonus
  , conditionMeleeBonus, conditionSpeedBonus, armorHurtCalculation
  , mergeItemQuant, listToolsToConsume, subtractIidfromGrps, sortIids
  , TileAction (..), parseTileAction
#ifdef EXPOSE_INTERNAL
    -- * Internal operations
  , valueAtEqpSlot, unknownAspect, countIidConsumed
#endif
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import           Data.Binary
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import           Data.Hashable (Hashable)
import qualified Data.Ix as Ix
import           GHC.Generics (Generic)

import qualified Game.LambdaHack.Common.ItemAspect as IA
import           Game.LambdaHack.Common.Kind
import           Game.LambdaHack.Common.Time
import           Game.LambdaHack.Common.Types
import qualified Game.LambdaHack.Content.ItemKind as IK
import qualified Game.LambdaHack.Content.TileKind as TK
import qualified Game.LambdaHack.Core.Dice as Dice
import           Game.LambdaHack.Definition.Ability (EqpSlot (..))
import qualified Game.LambdaHack.Definition.Ability as Ability
import           Game.LambdaHack.Definition.Defs
import           Game.LambdaHack.Definition.Flavour

-- | Game items in actor possesion or strewn around the dungeon.
-- The information contained in this time is available to the player
-- from the moment the item is first seen and is never mutated.
--
-- Some items are not created identified (@IdentityCovered@).
-- Then they are presented as having a template kind that is really
-- not their own, though usually close. Full kind information about
-- item's kind is available through the @ItemKindIx@ index once the item
-- is identified and full information about the value of item's aspect record
-- is available elsewhere (both @IdentityObvious@ and @IdentityCovered@
-- items may or may not need identification of their aspect record).
data Item = Item
  { Item -> ItemIdentity
jkind    :: ItemIdentity     -- ^ the kind of the item, or an indirection
  , Item -> Maybe FactionId
jfid     :: Maybe FactionId  -- ^ the faction that created the item, if any
  , Item -> Flavour
jflavour :: Flavour          -- ^ flavour, always the real one,
                                 --   it's not hidden; people may not recognize
                                 --   shape, but they remember colour and old
                                 --   vs fancy look
  }
  deriving (Int -> Item -> ShowS
[Item] -> ShowS
Item -> String
(Int -> Item -> ShowS)
-> (Item -> String) -> ([Item] -> ShowS) -> Show Item
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Item -> ShowS
showsPrec :: Int -> Item -> ShowS
$cshow :: Item -> String
show :: Item -> String
$cshowList :: [Item] -> ShowS
showList :: [Item] -> ShowS
Show, Item -> Item -> Bool
(Item -> Item -> Bool) -> (Item -> Item -> Bool) -> Eq Item
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Item -> Item -> Bool
== :: Item -> Item -> Bool
$c/= :: Item -> Item -> Bool
/= :: Item -> Item -> Bool
Eq, (forall x. Item -> Rep Item x)
-> (forall x. Rep Item x -> Item) -> Generic Item
forall x. Rep Item x -> Item
forall x. Item -> Rep Item x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Item -> Rep Item x
from :: forall x. Item -> Rep Item x
$cto :: forall x. Rep Item x -> Item
to :: forall x. Rep Item x -> Item
Generic)

instance Binary Item

-- | Either the explicit obvious kind of the item or the kind it's hidden under,
-- with the details covered under the index indirection.
data ItemIdentity =
    IdentityObvious (ContentId IK.ItemKind)
  | IdentityCovered ItemKindIx (ContentId IK.ItemKind)
  deriving (Int -> ItemIdentity -> ShowS
[ItemIdentity] -> ShowS
ItemIdentity -> String
(Int -> ItemIdentity -> ShowS)
-> (ItemIdentity -> String)
-> ([ItemIdentity] -> ShowS)
-> Show ItemIdentity
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ItemIdentity -> ShowS
showsPrec :: Int -> ItemIdentity -> ShowS
$cshow :: ItemIdentity -> String
show :: ItemIdentity -> String
$cshowList :: [ItemIdentity] -> ShowS
showList :: [ItemIdentity] -> ShowS
Show, ItemIdentity -> ItemIdentity -> Bool
(ItemIdentity -> ItemIdentity -> Bool)
-> (ItemIdentity -> ItemIdentity -> Bool) -> Eq ItemIdentity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ItemIdentity -> ItemIdentity -> Bool
== :: ItemIdentity -> ItemIdentity -> Bool
$c/= :: ItemIdentity -> ItemIdentity -> Bool
/= :: ItemIdentity -> ItemIdentity -> Bool
Eq, (forall x. ItemIdentity -> Rep ItemIdentity x)
-> (forall x. Rep ItemIdentity x -> ItemIdentity)
-> Generic ItemIdentity
forall x. Rep ItemIdentity x -> ItemIdentity
forall x. ItemIdentity -> Rep ItemIdentity x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ItemIdentity -> Rep ItemIdentity x
from :: forall x. ItemIdentity -> Rep ItemIdentity x
$cto :: forall x. Rep ItemIdentity x -> ItemIdentity
to :: forall x. Rep ItemIdentity x -> ItemIdentity
Generic)

instance Hashable ItemIdentity

instance Binary ItemIdentity

-- | The map of item ids to item aspect record. The full map is known
-- by the server.
type DiscoveryAspect = EM.EnumMap ItemId IA.AspectRecord

-- | An index of the kind identifier of an item. Clients have partial knowledge
-- how these idexes map to kind ids. They gain knowledge by identifying items.
-- The indexes and kind identifiers are 1-1.
newtype ItemKindIx = ItemKindIx Word16
  deriving (Int -> ItemKindIx -> ShowS
[ItemKindIx] -> ShowS
ItemKindIx -> String
(Int -> ItemKindIx -> ShowS)
-> (ItemKindIx -> String)
-> ([ItemKindIx] -> ShowS)
-> Show ItemKindIx
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ItemKindIx -> ShowS
showsPrec :: Int -> ItemKindIx -> ShowS
$cshow :: ItemKindIx -> String
show :: ItemKindIx -> String
$cshowList :: [ItemKindIx] -> ShowS
showList :: [ItemKindIx] -> ShowS
Show, ItemKindIx -> ItemKindIx -> Bool
(ItemKindIx -> ItemKindIx -> Bool)
-> (ItemKindIx -> ItemKindIx -> Bool) -> Eq ItemKindIx
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ItemKindIx -> ItemKindIx -> Bool
== :: ItemKindIx -> ItemKindIx -> Bool
$c/= :: ItemKindIx -> ItemKindIx -> Bool
/= :: ItemKindIx -> ItemKindIx -> Bool
Eq, Eq ItemKindIx
Eq ItemKindIx =>
(ItemKindIx -> ItemKindIx -> Ordering)
-> (ItemKindIx -> ItemKindIx -> Bool)
-> (ItemKindIx -> ItemKindIx -> Bool)
-> (ItemKindIx -> ItemKindIx -> Bool)
-> (ItemKindIx -> ItemKindIx -> Bool)
-> (ItemKindIx -> ItemKindIx -> ItemKindIx)
-> (ItemKindIx -> ItemKindIx -> ItemKindIx)
-> Ord ItemKindIx
ItemKindIx -> ItemKindIx -> Bool
ItemKindIx -> ItemKindIx -> Ordering
ItemKindIx -> ItemKindIx -> ItemKindIx
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: ItemKindIx -> ItemKindIx -> Ordering
compare :: ItemKindIx -> ItemKindIx -> Ordering
$c< :: ItemKindIx -> ItemKindIx -> Bool
< :: ItemKindIx -> ItemKindIx -> Bool
$c<= :: ItemKindIx -> ItemKindIx -> Bool
<= :: ItemKindIx -> ItemKindIx -> Bool
$c> :: ItemKindIx -> ItemKindIx -> Bool
> :: ItemKindIx -> ItemKindIx -> Bool
$c>= :: ItemKindIx -> ItemKindIx -> Bool
>= :: ItemKindIx -> ItemKindIx -> Bool
$cmax :: ItemKindIx -> ItemKindIx -> ItemKindIx
max :: ItemKindIx -> ItemKindIx -> ItemKindIx
$cmin :: ItemKindIx -> ItemKindIx -> ItemKindIx
min :: ItemKindIx -> ItemKindIx -> ItemKindIx
Ord, Int -> ItemKindIx
ItemKindIx -> Int
ItemKindIx -> [ItemKindIx]
ItemKindIx -> ItemKindIx
ItemKindIx -> ItemKindIx -> [ItemKindIx]
ItemKindIx -> ItemKindIx -> ItemKindIx -> [ItemKindIx]
(ItemKindIx -> ItemKindIx)
-> (ItemKindIx -> ItemKindIx)
-> (Int -> ItemKindIx)
-> (ItemKindIx -> Int)
-> (ItemKindIx -> [ItemKindIx])
-> (ItemKindIx -> ItemKindIx -> [ItemKindIx])
-> (ItemKindIx -> ItemKindIx -> [ItemKindIx])
-> (ItemKindIx -> ItemKindIx -> ItemKindIx -> [ItemKindIx])
-> Enum ItemKindIx
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: ItemKindIx -> ItemKindIx
succ :: ItemKindIx -> ItemKindIx
$cpred :: ItemKindIx -> ItemKindIx
pred :: ItemKindIx -> ItemKindIx
$ctoEnum :: Int -> ItemKindIx
toEnum :: Int -> ItemKindIx
$cfromEnum :: ItemKindIx -> Int
fromEnum :: ItemKindIx -> Int
$cenumFrom :: ItemKindIx -> [ItemKindIx]
enumFrom :: ItemKindIx -> [ItemKindIx]
$cenumFromThen :: ItemKindIx -> ItemKindIx -> [ItemKindIx]
enumFromThen :: ItemKindIx -> ItemKindIx -> [ItemKindIx]
$cenumFromTo :: ItemKindIx -> ItemKindIx -> [ItemKindIx]
enumFromTo :: ItemKindIx -> ItemKindIx -> [ItemKindIx]
$cenumFromThenTo :: ItemKindIx -> ItemKindIx -> ItemKindIx -> [ItemKindIx]
enumFromThenTo :: ItemKindIx -> ItemKindIx -> ItemKindIx -> [ItemKindIx]
Enum, Ord ItemKindIx
Ord ItemKindIx =>
((ItemKindIx, ItemKindIx) -> [ItemKindIx])
-> ((ItemKindIx, ItemKindIx) -> ItemKindIx -> Int)
-> ((ItemKindIx, ItemKindIx) -> ItemKindIx -> Int)
-> ((ItemKindIx, ItemKindIx) -> ItemKindIx -> Bool)
-> ((ItemKindIx, ItemKindIx) -> Int)
-> ((ItemKindIx, ItemKindIx) -> Int)
-> Ix ItemKindIx
(ItemKindIx, ItemKindIx) -> Int
(ItemKindIx, ItemKindIx) -> [ItemKindIx]
(ItemKindIx, ItemKindIx) -> ItemKindIx -> Bool
(ItemKindIx, ItemKindIx) -> ItemKindIx -> Int
forall a.
Ord a =>
((a, a) -> [a])
-> ((a, a) -> a -> Int)
-> ((a, a) -> a -> Int)
-> ((a, a) -> a -> Bool)
-> ((a, a) -> Int)
-> ((a, a) -> Int)
-> Ix a
$crange :: (ItemKindIx, ItemKindIx) -> [ItemKindIx]
range :: (ItemKindIx, ItemKindIx) -> [ItemKindIx]
$cindex :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Int
index :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Int
$cunsafeIndex :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Int
unsafeIndex :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Int
$cinRange :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Bool
inRange :: (ItemKindIx, ItemKindIx) -> ItemKindIx -> Bool
$crangeSize :: (ItemKindIx, ItemKindIx) -> Int
rangeSize :: (ItemKindIx, ItemKindIx) -> Int
$cunsafeRangeSize :: (ItemKindIx, ItemKindIx) -> Int
unsafeRangeSize :: (ItemKindIx, ItemKindIx) -> Int
Ix.Ix, Eq ItemKindIx
Eq ItemKindIx =>
(Int -> ItemKindIx -> Int)
-> (ItemKindIx -> Int) -> Hashable ItemKindIx
Int -> ItemKindIx -> Int
ItemKindIx -> Int
forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
$chashWithSalt :: Int -> ItemKindIx -> Int
hashWithSalt :: Int -> ItemKindIx -> Int
$chash :: ItemKindIx -> Int
hash :: ItemKindIx -> Int
Hashable, Get ItemKindIx
[ItemKindIx] -> Put
ItemKindIx -> Put
(ItemKindIx -> Put)
-> Get ItemKindIx -> ([ItemKindIx] -> Put) -> Binary ItemKindIx
forall t. (t -> Put) -> Get t -> ([t] -> Put) -> Binary t
$cput :: ItemKindIx -> Put
put :: ItemKindIx -> Put
$cget :: Get ItemKindIx
get :: Get ItemKindIx
$cputList :: [ItemKindIx] -> Put
putList :: [ItemKindIx] -> Put
Binary)

-- | The secret part of the information about an item. If a faction
-- knows the aspect record of the item, this is the complete secret information.
-- Items that don't need second identification (the @kmConst@ flag is set)
-- may be identified or not and both cases are OK (their display flavour
-- will differ and that may be the point).
data ItemDisco =
    ItemDiscoFull IA.AspectRecord
  | ItemDiscoMean IA.KindMean
 deriving (Int -> ItemDisco -> ShowS
[ItemDisco] -> ShowS
ItemDisco -> String
(Int -> ItemDisco -> ShowS)
-> (ItemDisco -> String)
-> ([ItemDisco] -> ShowS)
-> Show ItemDisco
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ItemDisco -> ShowS
showsPrec :: Int -> ItemDisco -> ShowS
$cshow :: ItemDisco -> String
show :: ItemDisco -> String
$cshowList :: [ItemDisco] -> ShowS
showList :: [ItemDisco] -> ShowS
Show, Eq ItemDisco
Eq ItemDisco =>
(ItemDisco -> ItemDisco -> Ordering)
-> (ItemDisco -> ItemDisco -> Bool)
-> (ItemDisco -> ItemDisco -> Bool)
-> (ItemDisco -> ItemDisco -> Bool)
-> (ItemDisco -> ItemDisco -> Bool)
-> (ItemDisco -> ItemDisco -> ItemDisco)
-> (ItemDisco -> ItemDisco -> ItemDisco)
-> Ord ItemDisco
ItemDisco -> ItemDisco -> Bool
ItemDisco -> ItemDisco -> Ordering
ItemDisco -> ItemDisco -> ItemDisco
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: ItemDisco -> ItemDisco -> Ordering
compare :: ItemDisco -> ItemDisco -> Ordering
$c< :: ItemDisco -> ItemDisco -> Bool
< :: ItemDisco -> ItemDisco -> Bool
$c<= :: ItemDisco -> ItemDisco -> Bool
<= :: ItemDisco -> ItemDisco -> Bool
$c> :: ItemDisco -> ItemDisco -> Bool
> :: ItemDisco -> ItemDisco -> Bool
$c>= :: ItemDisco -> ItemDisco -> Bool
>= :: ItemDisco -> ItemDisco -> Bool
$cmax :: ItemDisco -> ItemDisco -> ItemDisco
max :: ItemDisco -> ItemDisco -> ItemDisco
$cmin :: ItemDisco -> ItemDisco -> ItemDisco
min :: ItemDisco -> ItemDisco -> ItemDisco
Ord, ItemDisco -> ItemDisco -> Bool
(ItemDisco -> ItemDisco -> Bool)
-> (ItemDisco -> ItemDisco -> Bool) -> Eq ItemDisco
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ItemDisco -> ItemDisco -> Bool
== :: ItemDisco -> ItemDisco -> Bool
$c/= :: ItemDisco -> ItemDisco -> Bool
/= :: ItemDisco -> ItemDisco -> Bool
Eq)

-- No speedup from making fields non-strict.
-- | Full information about an item.
data ItemFull = ItemFull
  { ItemFull -> Item
itemBase    :: Item
  , ItemFull -> ContentId ItemKind
itemKindId  :: ContentId IK.ItemKind
  , ItemFull -> ItemKind
itemKind    :: IK.ItemKind
  , ItemFull -> ItemDisco
itemDisco   :: ItemDisco
  , ItemFull -> Bool
itemSuspect :: Bool
  }
  deriving Int -> ItemFull -> ShowS
[ItemFull] -> ShowS
ItemFull -> String
(Int -> ItemFull -> ShowS)
-> (ItemFull -> String) -> ([ItemFull] -> ShowS) -> Show ItemFull
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ItemFull -> ShowS
showsPrec :: Int -> ItemFull -> ShowS
$cshow :: ItemFull -> String
show :: ItemFull -> String
$cshowList :: [ItemFull] -> ShowS
showList :: [ItemFull] -> ShowS
Show

type ItemFullKit = (ItemFull, ItemQuant)

-- | The map of item kind indexes to item kind ids.
-- The full map, as known by the server, is 1-1.
-- Because it's sparse and changes, we don't represent it as an (unboxed)
-- vector, until it becomes a bottleneck (if ever, likely on JS, where only
-- vectors are fast).
type DiscoveryKind = EM.EnumMap ItemKindIx (ContentId IK.ItemKind)

-- | The map of item kind indexes to identifiers of items that have that kind.
-- Used to update data about items when their kinds become known, e.g.,
-- AI item use benefit data.
type ItemIxMap = EM.EnumMap ItemKindIx (ES.EnumSet ItemId)

-- | The fields are, in order:
-- 1. whether the item should be kept in equipment (not in stash)
-- 2. the total benefit from picking the item up (to use or to put in equipment)
-- 3. the benefit of applying the item to self
-- 4. the (usually negative, for him) value of hitting a foe in melee with it
-- 5. the (usually negative, for him) value of flinging the item at an opponent
data Benefit = Benefit
  { Benefit -> Bool
benInEqp  :: Bool
  , Benefit -> Double
benPickup :: Double
  , Benefit -> Double
benApply  :: Double
  , Benefit -> Double
benMelee  :: Double
  , Benefit -> Double
benFling  :: Double
  }
  deriving (Int -> Benefit -> ShowS
[Benefit] -> ShowS
Benefit -> String
(Int -> Benefit -> ShowS)
-> (Benefit -> String) -> ([Benefit] -> ShowS) -> Show Benefit
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Benefit -> ShowS
showsPrec :: Int -> Benefit -> ShowS
$cshow :: Benefit -> String
show :: Benefit -> String
$cshowList :: [Benefit] -> ShowS
showList :: [Benefit] -> ShowS
Show, (forall x. Benefit -> Rep Benefit x)
-> (forall x. Rep Benefit x -> Benefit) -> Generic Benefit
forall x. Rep Benefit x -> Benefit
forall x. Benefit -> Rep Benefit x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Benefit -> Rep Benefit x
from :: forall x. Benefit -> Rep Benefit x
$cto :: forall x. Rep Benefit x -> Benefit
to :: forall x. Rep Benefit x -> Benefit
Generic)

instance Binary Benefit

type DiscoveryBenefit = EM.EnumMap ItemId Benefit

-- | The absolute level's local time at which an item's copy becomes
-- operational again. Even if item is not identified and so its timeout
-- unknown, it's enough to compare this to the local level time
-- to learn whether an item is recharged.
--
-- This schema causes timeout jumps for items in stash, but timeout
-- is reset when items move, so this is a minor problem.
-- Global time can't be used even only for items in stash,
-- or exploit would be possible when an actor on a desolate level waits
-- to recharge items for actors on a busy level. It's probably
-- impossible to avoid such exploits or, otherwise, timeout jumps,
-- particularly for faction where many actors move on many levels
-- and so an item in stash is not used by a single actor at a time.
newtype ItemTimer = ItemTimer {ItemTimer -> Time
itemTimer :: Time}
  deriving (Int -> ItemTimer -> ShowS
[ItemTimer] -> ShowS
ItemTimer -> String
(Int -> ItemTimer -> ShowS)
-> (ItemTimer -> String)
-> ([ItemTimer] -> ShowS)
-> Show ItemTimer
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ItemTimer -> ShowS
showsPrec :: Int -> ItemTimer -> ShowS
$cshow :: ItemTimer -> String
show :: ItemTimer -> String
$cshowList :: [ItemTimer] -> ShowS
showList :: [ItemTimer] -> ShowS
Show, ItemTimer -> ItemTimer -> Bool
(ItemTimer -> ItemTimer -> Bool)
-> (ItemTimer -> ItemTimer -> Bool) -> Eq ItemTimer
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ItemTimer -> ItemTimer -> Bool
== :: ItemTimer -> ItemTimer -> Bool
$c/= :: ItemTimer -> ItemTimer -> Bool
/= :: ItemTimer -> ItemTimer -> Bool
Eq, Get ItemTimer
[ItemTimer] -> Put
ItemTimer -> Put
(ItemTimer -> Put)
-> Get ItemTimer -> ([ItemTimer] -> Put) -> Binary ItemTimer
forall t. (t -> Put) -> Get t -> ([t] -> Put) -> Binary t
$cput :: ItemTimer -> Put
put :: ItemTimer -> Put
$cget :: Get ItemTimer
get :: Get ItemTimer
$cputList :: [ItemTimer] -> Put
putList :: [ItemTimer] -> Put
Binary)

type ItemTimers = [ItemTimer]

-- | Number of items in a bag, together with recharging timer, in case of
-- items that need recharging, exists only temporarily or auto-activate
-- at regular intervals. Data invariant: the length of the timer
-- should be less or equal to the number of items.
type ItemQuant = (Int, ItemTimers)

-- | A bag of items, e.g., one of the stores of an actor or the items
-- on a particular floor position or embedded in a particular map tile.
type ItemBag = EM.EnumMap ItemId ItemQuant

-- | All items in the dungeon (including those carried by actors),
-- indexed by item identifier.
type ItemDict = EM.EnumMap ItemId Item

toItemKindIx :: Word16 -> ItemKindIx
{-# INLINE toItemKindIx #-}
toItemKindIx :: Word16 -> ItemKindIx
toItemKindIx = Word16 -> ItemKindIx
ItemKindIx

quantSingle :: ItemQuant
quantSingle :: ItemQuant
quantSingle = (Int
1, [])

itemToFull6 :: COps -> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item
            -> ItemFull
itemToFull6 :: COps
-> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item -> ItemFull
itemToFull6 COps{ContentData ItemKind
coitem :: ContentData ItemKind
coitem :: COps -> ContentData ItemKind
coitem, ItemSpeedup
coItemSpeedup :: ItemSpeedup
coItemSpeedup :: COps -> ItemSpeedup
coItemSpeedup} DiscoveryKind
discoKind DiscoveryAspect
discoAspect ItemId
iid Item
itemBase =
  let (ContentId ItemKind
itemKindId, Bool
itemSuspect) = case Item -> ItemIdentity
jkind Item
itemBase of
        IdentityObvious ContentId ItemKind
ik -> (ContentId ItemKind
ik, Bool
False)
        IdentityCovered ItemKindIx
ix ContentId ItemKind
ik ->
          (ContentId ItemKind, Bool)
-> (ContentId ItemKind -> (ContentId ItemKind, Bool))
-> Maybe (ContentId ItemKind)
-> (ContentId ItemKind, Bool)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ContentId ItemKind
ik, Bool
True) (, Bool
False) (Maybe (ContentId ItemKind) -> (ContentId ItemKind, Bool))
-> Maybe (ContentId ItemKind) -> (ContentId ItemKind, Bool)
forall a b. (a -> b) -> a -> b
$ ItemKindIx
ix ItemKindIx -> DiscoveryKind -> Maybe (ContentId ItemKind)
forall k a. Enum k => k -> EnumMap k a -> Maybe a
`EM.lookup` DiscoveryKind
discoKind
      itemKind :: ItemKind
itemKind = ContentData ItemKind -> ContentId ItemKind -> ItemKind
forall a. ContentData a -> ContentId a -> a
okind ContentData ItemKind
coitem ContentId ItemKind
itemKindId
      km :: KindMean
km = ContentId ItemKind -> ItemSpeedup -> KindMean
getKindMean ContentId ItemKind
itemKindId ItemSpeedup
coItemSpeedup
      -- If the kind is not identified, we know nothing about the real
      -- aspect record, so we at least assume they are variable.
      itemAspectMean :: KindMean
itemAspectMean | Bool
itemSuspect = KindMean
km {IA.kmConst = False}
                     | Bool
otherwise = KindMean
km
      itemDisco :: ItemDisco
itemDisco = case ItemId -> DiscoveryAspect -> Maybe AspectRecord
forall k a. Enum k => k -> EnumMap k a -> Maybe a
EM.lookup ItemId
iid DiscoveryAspect
discoAspect of
        Just AspectRecord
itemAspect -> AspectRecord -> ItemDisco
ItemDiscoFull AspectRecord
itemAspect
        Maybe AspectRecord
Nothing -> KindMean -> ItemDisco
ItemDiscoMean KindMean
itemAspectMean
  in ItemFull {Bool
ContentId ItemKind
ItemKind
ItemDisco
Item
itemBase :: Item
itemKindId :: ContentId ItemKind
itemKind :: ItemKind
itemDisco :: ItemDisco
itemSuspect :: Bool
itemBase :: Item
itemKindId :: ContentId ItemKind
itemSuspect :: Bool
itemKind :: ItemKind
itemDisco :: ItemDisco
..}

aspectRecordFull :: ItemFull -> IA.AspectRecord
aspectRecordFull :: ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull =
  case ItemFull -> ItemDisco
itemDisco ItemFull
itemFull of
    ItemDiscoFull AspectRecord
itemAspect -> AspectRecord
itemAspect
    ItemDiscoMean KindMean
itemAspectMean -> KindMean -> AspectRecord
IA.kmMean KindMean
itemAspectMean

-- This ignores items that don't go into equipment, as determined in @benInEqp@.
-- They are removed from equipment elsewhere via @harmful@.
strongestSlot :: DiscoveryBenefit -> Ability.EqpSlot -> [(ItemId, ItemFullKit)]
              -> [(Int, (ItemId, ItemFullKit))]
strongestSlot :: DiscoveryBenefit
-> EqpSlot
-> [(ItemId, ItemFullKit)]
-> [(Int, (ItemId, ItemFullKit))]
strongestSlot DiscoveryBenefit
discoBenefit EqpSlot
eqpSlot [(ItemId, ItemFullKit)]
is =
  let f :: (ItemId, ItemFullKit) -> Maybe (Int, (ItemId, ItemFullKit))
f (ItemId
iid, (ItemFull
itemFull, ItemQuant
kit)) =
        let Benefit{Bool
benInEqp :: Benefit -> Bool
benInEqp :: Bool
benInEqp, Double
benPickup :: Benefit -> Double
benPickup :: Double
benPickup, Double
benMelee :: Benefit -> Double
benMelee :: Double
benMelee} = DiscoveryBenefit
discoBenefit DiscoveryBenefit -> ItemId -> Benefit
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ItemId
iid
        in if Bool -> Bool
not Bool
benInEqp
           then Maybe (Int, (ItemId, ItemFullKit))
forall a. Maybe a
Nothing
           else (Int, (ItemId, ItemFullKit)) -> Maybe (Int, (ItemId, ItemFullKit))
forall a. a -> Maybe a
Just ((Int, (ItemId, ItemFullKit))
 -> Maybe (Int, (ItemId, ItemFullKit)))
-> (Int, (ItemId, ItemFullKit))
-> Maybe (Int, (ItemId, ItemFullKit))
forall a b. (a -> b) -> a -> b
$
             let ben :: Int
ben = case EqpSlot
eqpSlot of
                   EqpSlot
EqpSlotWeaponFast ->
                       -- For equipping/unequipping the main reliable weapon,
                       -- we take into account not only melee damage,
                       -- but also timeout, aspects, etc.
                       Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
ceiling Double
benPickup
                   EqpSlot
EqpSlotWeaponBig ->
                       -- For equipping/unequipping the one-shot big hitter
                       -- weapon, we take into account only melee damage
                       -- and we don't even care if it's durable.
                       -- The backup is ready in the slot above, after all.
                       Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
ceiling (- Double
benMelee)
                   EqpSlot
_ -> EqpSlot -> AspectRecord -> Int
valueAtEqpSlot EqpSlot
eqpSlot (AspectRecord -> Int) -> AspectRecord -> Int
forall a b. (a -> b) -> a -> b
$ ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
                 idBonus :: Int
idBonus = if ItemFull -> Bool
itemSuspect ItemFull
itemFull then Int
1000 else Int
0
                 arItem :: AspectRecord
arItem = ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
                 -- Equip good uniques for flavour and fun from unique effects.
                 uniqueBonus :: Int
uniqueBonus = if Flag -> AspectRecord -> Bool
IA.checkFlag Flag
Ability.Unique AspectRecord
arItem Bool -> Bool -> Bool
&& Int
ben Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
20
                               then Int
1000
                               else Int
0
             in (Int
ben Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
idBonus Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
uniqueBonus, (ItemId
iid, (ItemFull
itemFull, ItemQuant
kit)))
  in ((Int, (ItemId, ItemFullKit))
 -> (Int, (ItemId, ItemFullKit)) -> Ordering)
-> [(Int, (ItemId, ItemFullKit))] -> [(Int, (ItemId, ItemFullKit))]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((Int, (ItemId, ItemFullKit))
 -> (Int, (ItemId, ItemFullKit)) -> Ordering)
-> (Int, (ItemId, ItemFullKit))
-> (Int, (ItemId, ItemFullKit))
-> Ordering
forall a b c. (a -> b -> c) -> b -> a -> c
flip (((Int, (ItemId, ItemFullKit))
  -> (Int, (ItemId, ItemFullKit)) -> Ordering)
 -> (Int, (ItemId, ItemFullKit))
 -> (Int, (ItemId, ItemFullKit))
 -> Ordering)
-> ((Int, (ItemId, ItemFullKit))
    -> (Int, (ItemId, ItemFullKit)) -> Ordering)
-> (Int, (ItemId, ItemFullKit))
-> (Int, (ItemId, ItemFullKit))
-> Ordering
forall a b. (a -> b) -> a -> b
$ ((Int, (ItemId, ItemFullKit)) -> Int)
-> (Int, (ItemId, ItemFullKit))
-> (Int, (ItemId, ItemFullKit))
-> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Int, (ItemId, ItemFullKit)) -> Int
forall a b. (a, b) -> a
fst) ([(Int, (ItemId, ItemFullKit))] -> [(Int, (ItemId, ItemFullKit))])
-> [(Int, (ItemId, ItemFullKit))] -> [(Int, (ItemId, ItemFullKit))]
forall a b. (a -> b) -> a -> b
$ ((ItemId, ItemFullKit) -> Maybe (Int, (ItemId, ItemFullKit)))
-> [(ItemId, ItemFullKit)] -> [(Int, (ItemId, ItemFullKit))]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ItemId, ItemFullKit) -> Maybe (Int, (ItemId, ItemFullKit))
f [(ItemId, ItemFullKit)]
is

valueAtEqpSlot :: EqpSlot -> IA.AspectRecord -> Int
valueAtEqpSlot :: EqpSlot -> AspectRecord -> Int
valueAtEqpSlot EqpSlot
eqpSlot arItem :: AspectRecord
arItem@IA.AspectRecord{Int
Maybe EqpSlot
Maybe (GroupName ItemKind)
Text
Flags
Skills
ThrowMod
aTimeout :: Int
aSkills :: Skills
aFlags :: Flags
aELabel :: Text
aToThrow :: ThrowMod
aPresentAs :: Maybe (GroupName ItemKind)
aEqpSlot :: Maybe EqpSlot
aTimeout :: AspectRecord -> Int
aSkills :: AspectRecord -> Skills
aFlags :: AspectRecord -> Flags
aELabel :: AspectRecord -> Text
aToThrow :: AspectRecord -> ThrowMod
aPresentAs :: AspectRecord -> Maybe (GroupName ItemKind)
aEqpSlot :: AspectRecord -> Maybe EqpSlot
..} =
  case EqpSlot
eqpSlot of
    EqpSlot
EqpSlotMove -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMove Skills
aSkills
    EqpSlot
EqpSlotMelee -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMelee Skills
aSkills
    EqpSlot
EqpSlotDisplace -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkDisplace Skills
aSkills
    EqpSlot
EqpSlotAlter -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkAlter Skills
aSkills
    EqpSlot
EqpSlotWait -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkWait Skills
aSkills
    EqpSlot
EqpSlotMoveItem -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMoveItem Skills
aSkills
    EqpSlot
EqpSlotProject -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkProject Skills
aSkills
    EqpSlot
EqpSlotApply -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkApply Skills
aSkills
    EqpSlot
EqpSlotSwimming -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkSwimming Skills
aSkills
    EqpSlot
EqpSlotFlying -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkFlying Skills
aSkills
    EqpSlot
EqpSlotHurtMelee -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkHurtMelee Skills
aSkills
    EqpSlot
EqpSlotArmorMelee -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkArmorMelee Skills
aSkills
    EqpSlot
EqpSlotArmorRanged -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkArmorRanged Skills
aSkills
    EqpSlot
EqpSlotMaxHP -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMaxHP Skills
aSkills
    EqpSlot
EqpSlotSpeed -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkSpeed Skills
aSkills
    EqpSlot
EqpSlotSight -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkSight Skills
aSkills
    EqpSlot
EqpSlotShine -> Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkShine Skills
aSkills
    EqpSlot
EqpSlotMiscBonus ->
      Int
aTimeout  -- usually better items have longer timeout
      Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkMaxCalm Skills
aSkills
      Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkSmell Skills
aSkills
      Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkNocto Skills
aSkills
          -- powerful, but hard to boost over aSight
    EqpSlot
EqpSlotWeaponFast -> String -> Int
forall a. HasCallStack => String -> a
error (String -> Int) -> String -> Int
forall a b. (a -> b) -> a -> b
$ String
"" String -> AspectRecord -> String
forall v. Show v => String -> v -> String
`showFailure` AspectRecord
arItem  -- sum of all benefits
    EqpSlot
EqpSlotWeaponBig -> String -> Int
forall a. HasCallStack => String -> a
error (String -> Int) -> String -> Int
forall a b. (a -> b) -> a -> b
$ String
"" String -> AspectRecord -> String
forall v. Show v => String -> v -> String
`showFailure` AspectRecord
arItem  -- sum of all benefits

itemTimerZero :: ItemTimer
itemTimerZero :: ItemTimer
itemTimerZero = Time -> ItemTimer
ItemTimer Time
timeZero

createItemTimer :: Time -> Delta Time -> ItemTimer
createItemTimer :: Time -> Delta Time -> ItemTimer
createItemTimer Time
localTime Delta Time
delta = Time -> ItemTimer
ItemTimer (Time -> ItemTimer) -> Time -> ItemTimer
forall a b. (a -> b) -> a -> b
$ Time
localTime Time -> Delta Time -> Time
`timeShift` Delta Time
delta

shiftItemTimer :: Delta Time -> ItemTimer -> ItemTimer
shiftItemTimer :: Delta Time -> ItemTimer -> ItemTimer
shiftItemTimer Delta Time
delta ItemTimer
t = Time -> ItemTimer
ItemTimer (Time -> ItemTimer) -> Time -> ItemTimer
forall a b. (a -> b) -> a -> b
$ ItemTimer -> Time
itemTimer ItemTimer
t Time -> Delta Time -> Time
`timeShift` Delta Time
delta

deltaOfItemTimer :: Time -> ItemTimer -> Delta Time
deltaOfItemTimer :: Time -> ItemTimer -> Delta Time
deltaOfItemTimer Time
localTime ItemTimer
t = Time -> Time -> Delta Time
timeDeltaToFrom (ItemTimer -> Time
itemTimer ItemTimer
t) Time
localTime

charging :: Time -> ItemTimer -> Bool
charging :: Time -> ItemTimer -> Bool
charging Time
localTime = (Time -> Time -> Bool
forall a. Ord a => a -> a -> Bool
> Time
localTime) (Time -> Bool) -> (ItemTimer -> Time) -> ItemTimer -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ItemTimer -> Time
itemTimer

ncharges :: Time -> ItemQuant -> Int
ncharges :: Time -> ItemQuant -> Int
ncharges Time
localTime (Int
itemK, [ItemTimer]
itemTimers) =
  Int
itemK Int -> Int -> Int
forall a. Num a => a -> a -> a
- [ItemTimer] -> Int
forall a. [a] -> Int
length ((ItemTimer -> Bool) -> [ItemTimer] -> [ItemTimer]
forall a. (a -> Bool) -> [a] -> [a]
filter (Time -> ItemTimer -> Bool
charging Time
localTime) [ItemTimer]
itemTimers)

hasCharge :: Time -> ItemQuant -> Bool
hasCharge :: Time -> ItemQuant -> Bool
hasCharge Time
localTime ItemQuant
kit = Time -> ItemQuant -> Int
ncharges Time
localTime ItemQuant
kit Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0

strongestMelee :: Bool -> Maybe DiscoveryBenefit -> Time
               -> [(ItemId, ItemFullKit)]
               -> [(Double, Bool, Int, Int, ItemId, ItemFullKit)]
strongestMelee :: Bool
-> Maybe DiscoveryBenefit
-> Time
-> [(ItemId, ItemFullKit)]
-> [(Double, Bool, Int, Int, ItemId, ItemFullKit)]
strongestMelee Bool
_ Maybe DiscoveryBenefit
_ Time
_ [] = []
strongestMelee Bool
ignoreCharges Maybe DiscoveryBenefit
mdiscoBenefit Time
localTime [(ItemId, ItemFullKit)]
kitAss =
  -- For fighting, as opposed to equipping, we value weapon only for
  -- its raw damage and harming effects and at this very moment only,
  -- not in the future. Hehce, we exclude discharged weapons.
  let f :: (ItemId, ItemFullKit)
-> Maybe (Double, Bool, Int, Int, ItemId, ItemFullKit)
f (ItemId
iid, ifk :: ItemFullKit
ifk@(ItemFull
itemFull, ItemQuant
kit)) =
        let rawDmg :: Double
rawDmg = ItemKind -> Double
IK.damageUsefulness (ItemKind -> Double) -> ItemKind -> Double
forall a b. (a -> b) -> a -> b
$ ItemFull -> ItemKind
itemKind ItemFull
itemFull
            unIDedBonus :: Double
unIDedBonus = if ItemFull -> Bool
itemSuspect ItemFull
itemFull then Double
1000 else Double
0
            totalValue :: Double
totalValue = case Maybe DiscoveryBenefit
mdiscoBenefit of
              Just DiscoveryBenefit
discoBenefit ->
                let Benefit{Double
benMelee :: Benefit -> Double
benMelee :: Double
benMelee} = DiscoveryBenefit
discoBenefit DiscoveryBenefit -> ItemId -> Benefit
forall k a. Enum k => EnumMap k a -> k -> a
EM.! ItemId
iid
                in Double
benMelee Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
unIDedBonus
              Maybe DiscoveryBenefit
Nothing -> - Double
rawDmg  -- special case: not interested about ID
            arItem :: AspectRecord
arItem = ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
            timeout :: Int
timeout = AspectRecord -> Int
IA.aTimeout AspectRecord
arItem
            -- This is crucial for weapons for which AI is too silly
            -- to value the effects at more than 0, even though they are strong
            -- and also to prefer weapons with burn or wound over pure damage,
            -- which is a good rule of thumb before late game at least.
            hasEffect :: Bool
hasEffect = (Effect -> Bool) -> [Effect] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Effect -> Bool
IK.forApplyEffect
                            (ItemKind -> [Effect]
IK.ieffects (ItemKind -> [Effect]) -> ItemKind -> [Effect]
forall a b. (a -> b) -> a -> b
$ ItemFull -> ItemKind
itemKind ItemFull
itemFull)
            ncha :: Int
ncha = Time -> ItemQuant -> Int
ncharges Time
localTime ItemQuant
kit
        in if Bool
ignoreCharges Bool -> Bool -> Bool
|| Int
ncha Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
           then (Double, Bool, Int, Int, ItemId, ItemFullKit)
-> Maybe (Double, Bool, Int, Int, ItemId, ItemFullKit)
forall a. a -> Maybe a
Just (Double
totalValue, Bool
hasEffect, Int
timeout, Int
ncha, ItemId
iid, ItemFullKit
ifk)
           else Maybe (Double, Bool, Int, Int, ItemId, ItemFullKit)
forall a. Maybe a
Nothing
  -- We can't filter out weapons that are not harmful to victim
  -- (@benMelee >= 0), because actors use them if nothing else available,
  -- e.g., geysers, bees. This is intended and fun.
  in ((Double, Bool, Int, Int, ItemId, ItemFullKit)
 -> (Double, Bool, Int, ContentId ItemKind))
-> [(Double, Bool, Int, Int, ItemId, ItemFullKit)]
-> [(Double, Bool, Int, Int, ItemId, ItemFullKit)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\(Double
value, Bool
hasEffect, Int
timeout, Int
_, ItemId
_, (ItemFull
itemFull, ItemQuant
_)) ->
                -- Weapon with higher timeout activated first to increase
                -- the chance of using it again during this fight.
                -- No timeout is ever better, because no wait incurred.
                -- Optimal packing problem: start with the biggest.
                let timN :: Int
timN = if Int
timeout Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then -Int
99999 else - Int
timeout
                in (Double
value, Bool -> Bool
not Bool
hasEffect, Int
timN, ItemFull -> ContentId ItemKind
itemKindId ItemFull
itemFull))
            (((ItemId, ItemFullKit)
 -> Maybe (Double, Bool, Int, Int, ItemId, ItemFullKit))
-> [(ItemId, ItemFullKit)]
-> [(Double, Bool, Int, Int, ItemId, ItemFullKit)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ItemId, ItemFullKit)
-> Maybe (Double, Bool, Int, Int, ItemId, ItemFullKit)
f [(ItemId, ItemFullKit)]
kitAss)

unknownAspect :: (IK.Aspect -> [Dice.Dice]) -> ItemFull -> Bool
unknownAspect :: (Aspect -> [Dice]) -> ItemFull -> Bool
unknownAspect Aspect -> [Dice]
f itemFull :: ItemFull
itemFull@ItemFull{itemKind :: ItemFull -> ItemKind
itemKind=IK.ItemKind{[Aspect]
iaspects :: [Aspect]
iaspects :: ItemKind -> [Aspect]
iaspects}, Bool
ContentId ItemKind
ItemDisco
Item
itemBase :: ItemFull -> Item
itemKindId :: ItemFull -> ContentId ItemKind
itemDisco :: ItemFull -> ItemDisco
itemSuspect :: ItemFull -> Bool
itemBase :: Item
itemKindId :: ContentId ItemKind
itemDisco :: ItemDisco
itemSuspect :: Bool
..} =
  case ItemDisco
itemDisco of
    ItemDiscoMean IA.KindMean{Bool
kmConst :: KindMean -> Bool
kmConst :: Bool
kmConst} ->
      let arItem :: AspectRecord
arItem = ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
          unknown :: Dice -> Bool
unknown Dice
x = let (Int
minD, Int
maxD) = Dice -> (Int, Int)
Dice.infsupDice Dice
x
                      in Int
minD Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
maxD
      in Bool
itemSuspect Bool -> Bool -> Bool
&& Bool -> Bool
not (Flag -> AspectRecord -> Bool
IA.checkFlag Flag
Ability.MinorAspects AspectRecord
arItem)
         Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
kmConst Bool -> Bool -> Bool
&& [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or ((Aspect -> [Bool]) -> [Aspect] -> [Bool]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((Dice -> Bool) -> [Dice] -> [Bool]
forall a b. (a -> b) -> [a] -> [b]
map Dice -> Bool
unknown ([Dice] -> [Bool]) -> (Aspect -> [Dice]) -> Aspect -> [Bool]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Aspect -> [Dice]
f) [Aspect]
iaspects)
    ItemDiscoFull{} -> Bool
False  -- all known

-- We assume @SkHurtMelee@ never appears inside @Odds@. If it does,
-- not much harm.
unknownMeleeBonus :: [ItemFull] -> Bool
unknownMeleeBonus :: [ItemFull] -> Bool
unknownMeleeBonus =
  let p :: Aspect -> [Dice]
p (IK.AddSkill Skill
Ability.SkHurtMelee Dice
k) = [Dice
k]
      p Aspect
_ = []
      f :: ItemFull -> Bool -> Bool
f ItemFull
itemFull Bool
b = Bool
b Bool -> Bool -> Bool
|| (Aspect -> [Dice]) -> ItemFull -> Bool
unknownAspect Aspect -> [Dice]
p ItemFull
itemFull
  in (ItemFull -> Bool -> Bool) -> Bool -> [ItemFull] -> Bool
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ItemFull -> Bool -> Bool
f Bool
False

-- We assume @SkSpeed@ never appears inside @Odds@. If it does,
-- not much harm.
unknownSpeedBonus :: [ItemFull] -> Bool
unknownSpeedBonus :: [ItemFull] -> Bool
unknownSpeedBonus =
  let p :: Aspect -> [Dice]
p (IK.AddSkill Skill
Ability.SkSpeed Dice
k) = [Dice
k]
      p Aspect
_ = []
      f :: ItemFull -> Bool -> Bool
f ItemFull
itemFull Bool
b = Bool
b Bool -> Bool -> Bool
|| (Aspect -> [Dice]) -> ItemFull -> Bool
unknownAspect Aspect -> [Dice]
p ItemFull
itemFull
  in (ItemFull -> Bool -> Bool) -> Bool -> [ItemFull] -> Bool
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ItemFull -> Bool -> Bool
f Bool
False

conditionMeleeBonus :: [ItemFullKit] -> Int
conditionMeleeBonus :: [ItemFullKit] -> Int
conditionMeleeBonus [ItemFullKit]
kitAss =
  let f :: (ItemFull, (Int, b)) -> Int -> Int
f (ItemFull
itemFull, (Int
itemK, b
_)) Int
k =
        let arItem :: AspectRecord
arItem = ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
        in if Flag -> AspectRecord -> Bool
IA.checkFlag Flag
Ability.Condition AspectRecord
arItem
           then Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
itemK Int -> Int -> Int
forall a. Num a => a -> a -> a
* Skill -> AspectRecord -> Int
IA.getSkill Skill
Ability.SkHurtMelee AspectRecord
arItem
           else Int
k
  in (ItemFullKit -> Int -> Int) -> Int -> [ItemFullKit] -> Int
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ItemFullKit -> Int -> Int
forall {b}. (ItemFull, (Int, b)) -> Int -> Int
f Int
0 [ItemFullKit]
kitAss

conditionSpeedBonus :: [ItemFullKit] -> Int
conditionSpeedBonus :: [ItemFullKit] -> Int
conditionSpeedBonus [ItemFullKit]
kitAss =
  let f :: (ItemFull, (Int, b)) -> Int -> Int
f (ItemFull
itemFull, (Int
itemK, b
_)) Int
k =
        let arItem :: AspectRecord
arItem = ItemFull -> AspectRecord
aspectRecordFull ItemFull
itemFull
        in if Flag -> AspectRecord -> Bool
IA.checkFlag Flag
Ability.Condition AspectRecord
arItem
           then Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
itemK Int -> Int -> Int
forall a. Num a => a -> a -> a
* Skill -> AspectRecord -> Int
IA.getSkill Skill
Ability.SkSpeed AspectRecord
arItem
           else Int
k
  in (ItemFullKit -> Int -> Int) -> Int -> [ItemFullKit] -> Int
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ItemFullKit -> Int -> Int
forall {b}. (ItemFull, (Int, b)) -> Int -> Int
f Int
0 [ItemFullKit]
kitAss

-- | Damage calculation. The armor and hurt skills are additive.
-- They can't be multiplicative, because then 100% armor would minimize
-- damage regardless of even 200% hurt skill.
-- However, additive skills make the relative effectiveness of weapons
-- dependent on the enemy, so even with -100% hurt skill a kinetic weapon
-- can't be removed from the list, because an enemy may have
-- negative armor skill. This is bad, but also KISS.
armorHurtCalculation :: Bool -> Ability.Skills -> Ability.Skills -> Int
armorHurtCalculation :: Bool -> Skills -> Skills -> Int
armorHurtCalculation Bool
proj Skills
sMaxSk Skills
tMaxSk =
  let trim200 :: a -> a
trim200 a
n = a -> a -> a
forall a. Ord a => a -> a -> a
min a
200 (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ a -> a -> a
forall a. Ord a => a -> a -> a
max (-a
200) a
n
      itemBonus :: Int
itemBonus =
        Int -> Int
forall {a}. (Ord a, Num a) => a -> a
trim200 (Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkHurtMelee Skills
sMaxSk)
        Int -> Int -> Int
forall a. Num a => a -> a -> a
- if Bool
proj
          then Int -> Int
forall {a}. (Ord a, Num a) => a -> a
trim200 (Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkArmorRanged Skills
tMaxSk)
          else Int -> Int
forall {a}. (Ord a, Num a) => a -> a
trim200 (Skill -> Skills -> Int
Ability.getSk Skill
Ability.SkArmorMelee Skills
tMaxSk)
  in Int
100 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (-Int
95) Int
itemBonus  -- at least 5% of damage gets through

mergeItemQuant :: ItemQuant -> ItemQuant -> ItemQuant
mergeItemQuant :: ItemQuant -> ItemQuant -> ItemQuant
mergeItemQuant (Int
k2, [ItemTimer]
it2) (Int
k1, [ItemTimer]
it1) = (Int
k1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
k2, [ItemTimer]
it1 [ItemTimer] -> [ItemTimer] -> [ItemTimer]
forall a. [a] -> [a] -> [a]
++ [ItemTimer]
it2)

listToolsToConsume :: [(ItemId, ItemFullKit)] -> [(ItemId, ItemFullKit)]
                   -> [((CStore, Bool), (ItemId, ItemFullKit))]
listToolsToConsume :: [(ItemId, ItemFullKit)]
-> [(ItemId, ItemFullKit)]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
listToolsToConsume [(ItemId, ItemFullKit)]
kitAssG [(ItemId, ItemFullKit)]
kitAssE =
  let isDurable :: (a, (ItemFull, b)) -> Bool
isDurable = Flag -> AspectRecord -> Bool
IA.checkFlag Flag
Ability.Durable
                  (AspectRecord -> Bool)
-> ((a, (ItemFull, b)) -> AspectRecord)
-> (a, (ItemFull, b))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ItemFull -> AspectRecord
aspectRecordFull (ItemFull -> AspectRecord)
-> ((a, (ItemFull, b)) -> ItemFull)
-> (a, (ItemFull, b))
-> AspectRecord
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ItemFull, b) -> ItemFull
forall a b. (a, b) -> a
fst ((ItemFull, b) -> ItemFull)
-> ((a, (ItemFull, b)) -> (ItemFull, b))
-> (a, (ItemFull, b))
-> ItemFull
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, (ItemFull, b)) -> (ItemFull, b)
forall a b. (a, b) -> b
snd
      ([(ItemId, ItemFullKit)]
kitAssGT, [(ItemId, ItemFullKit)]
kitAssGF) = ((ItemId, ItemFullKit) -> Bool)
-> [(ItemId, ItemFullKit)]
-> ([(ItemId, ItemFullKit)], [(ItemId, ItemFullKit)])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (ItemId, ItemFullKit) -> Bool
forall {a} {b}. (a, (ItemFull, b)) -> Bool
isDurable [(ItemId, ItemFullKit)]
kitAssG
      ([(ItemId, ItemFullKit)]
kitAssET, [(ItemId, ItemFullKit)]
kitAssEF) = ((ItemId, ItemFullKit) -> Bool)
-> [(ItemId, ItemFullKit)]
-> ([(ItemId, ItemFullKit)], [(ItemId, ItemFullKit)])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (ItemId, ItemFullKit) -> Bool
forall {a} {b}. (a, (ItemFull, b)) -> Bool
isDurable [(ItemId, ItemFullKit)]
kitAssE
      -- Non-durable tools take precedence, because durable
      -- are applied and, usually being weapons,
      -- may be harmful or may have unintended effects.
      -- CGround takes precedence, too.
  in [(CStore, Bool)]
-> [(ItemId, ItemFullKit)]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a b. [a] -> [b] -> [(a, b)]
zip ((CStore, Bool) -> [(CStore, Bool)]
forall a. a -> [a]
repeat (CStore
CGround, Bool
False)) [(ItemId, ItemFullKit)]
kitAssGF
     [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a. [a] -> [a] -> [a]
++ [(CStore, Bool)]
-> [(ItemId, ItemFullKit)]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a b. [a] -> [b] -> [(a, b)]
zip ((CStore, Bool) -> [(CStore, Bool)]
forall a. a -> [a]
repeat (CStore
CEqp, Bool
False)) [(ItemId, ItemFullKit)]
kitAssEF
     [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a. [a] -> [a] -> [a]
++ [(CStore, Bool)]
-> [(ItemId, ItemFullKit)]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a b. [a] -> [b] -> [(a, b)]
zip ((CStore, Bool) -> [(CStore, Bool)]
forall a. a -> [a]
repeat (CStore
CGround, Bool
True)) [(ItemId, ItemFullKit)]
kitAssGT
     [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a. [a] -> [a] -> [a]
++ [(CStore, Bool)]
-> [(ItemId, ItemFullKit)]
-> [((CStore, Bool), (ItemId, ItemFullKit))]
forall a b. [a] -> [b] -> [(a, b)]
zip ((CStore, Bool) -> [(CStore, Bool)]
forall a. a -> [a]
repeat (CStore
CEqp, Bool
True)) [(ItemId, ItemFullKit)]
kitAssET

countIidConsumed :: ItemFullKit
                 -> [(Bool, Int, GroupName IK.ItemKind)]
                 -> (Int, Int, [(Bool, Int, GroupName IK.ItemKind)])
countIidConsumed :: ItemFullKit
-> [(Bool, Int, GroupName ItemKind)]
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
countIidConsumed (ItemFull{ItemKind
itemKind :: ItemFull -> ItemKind
itemKind :: ItemKind
itemKind}, (Int
k, [ItemTimer]
_)) [(Bool, Int, GroupName ItemKind)]
grps0 =
  let hasGroup :: GroupName ItemKind -> Bool
hasGroup GroupName ItemKind
grp =
        Bool -> (Int -> Bool) -> Maybe Int -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0) (Maybe Int -> Bool) -> Maybe Int -> Bool
forall a b. (a -> b) -> a -> b
$ GroupName ItemKind -> [(GroupName ItemKind, Int)] -> Maybe Int
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup GroupName ItemKind
grp ([(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
      matchGroup :: (Int, Int, [(Bool, Int, GroupName ItemKind)])
-> (Bool, Int, GroupName ItemKind)
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
matchGroup (Int
nToApplyIfDurable, Int
nToDestroyAlways, [(Bool, Int, GroupName ItemKind)]
grps)
                 (Bool
destroyAlways, Int
n, GroupName ItemKind
grp) =
        if GroupName ItemKind -> Bool
hasGroup GroupName ItemKind
grp
        then let mkn :: Int
mkn = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
k Int
n  -- even if durable, use each copy only once
                 grps2 :: [(Bool, Int, GroupName ItemKind)]
grps2 = if Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
mkn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
                         then (Bool
destroyAlways, Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
mkn, GroupName ItemKind
grp) (Bool, Int, GroupName ItemKind)
-> [(Bool, Int, GroupName ItemKind)]
-> [(Bool, Int, GroupName ItemKind)]
forall a. a -> [a] -> [a]
: [(Bool, Int, GroupName ItemKind)]
grps
                         else [(Bool, Int, GroupName ItemKind)]
grps
             in if Bool
destroyAlways
                then ( Int
nToApplyIfDurable
                     , Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
nToDestroyAlways Int
mkn
                     , [(Bool, Int, GroupName ItemKind)]
grps2 )
                else ( Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
nToApplyIfDurable Int
mkn
                     , Int
nToDestroyAlways
                     , [(Bool, Int, GroupName ItemKind)]
grps2 )
        else ( Int
nToApplyIfDurable
             , Int
nToDestroyAlways
             , (Bool
destroyAlways, Int
n, GroupName ItemKind
grp) (Bool, Int, GroupName ItemKind)
-> [(Bool, Int, GroupName ItemKind)]
-> [(Bool, Int, GroupName ItemKind)]
forall a. a -> [a] -> [a]
: [(Bool, Int, GroupName ItemKind)]
grps )
  in ((Int, Int, [(Bool, Int, GroupName ItemKind)])
 -> (Bool, Int, GroupName ItemKind)
 -> (Int, Int, [(Bool, Int, GroupName ItemKind)]))
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
-> [(Bool, Int, GroupName ItemKind)]
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Int, Int, [(Bool, Int, GroupName ItemKind)])
-> (Bool, Int, GroupName ItemKind)
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
matchGroup (Int
0, Int
0, []) [(Bool, Int, GroupName ItemKind)]
grps0

subtractIidfromGrps :: ( EM.EnumMap CStore ItemBag
                       , [(CStore, (ItemId, ItemFull))]
                       , [(Bool, Int, GroupName IK.ItemKind)] )
                    -> ((CStore, Bool), (ItemId, ItemFullKit))
                    -> ( EM.EnumMap CStore ItemBag
                       , [(CStore, (ItemId, ItemFull))]
                       , [(Bool, Int, GroupName IK.ItemKind)] )
subtractIidfromGrps :: (EnumMap CStore ItemBag, [(CStore, (ItemId, ItemFull))],
 [(Bool, Int, GroupName ItemKind)])
-> ((CStore, Bool), (ItemId, ItemFullKit))
-> (EnumMap CStore ItemBag, [(CStore, (ItemId, ItemFull))],
    [(Bool, Int, GroupName ItemKind)])
subtractIidfromGrps (EnumMap CStore ItemBag
bagsToLose1, [(CStore, (ItemId, ItemFull))]
iidsToApply1, [(Bool, Int, GroupName ItemKind)]
grps1)
                    ((CStore
store, Bool
durable), (ItemId
iid, itemFullKit :: ItemFullKit
itemFullKit@(ItemFull
itemFull, (Int
_, [ItemTimer]
it)))) =
  let (Int
nToApplyIfDurable, Int
nToDestroyAlways, [(Bool, Int, GroupName ItemKind)]
grps2) =
        ItemFullKit
-> [(Bool, Int, GroupName ItemKind)]
-> (Int, Int, [(Bool, Int, GroupName ItemKind)])
countIidConsumed ItemFullKit
itemFullKit [(Bool, Int, GroupName ItemKind)]
grps1
      (Int
nToApply, Int
nToDestroy) = if Bool
durable
                               then (Int
nToApplyIfDurable, Int
nToDestroyAlways)
                               else (Int
0, Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
nToApplyIfDurable Int
nToDestroyAlways)
  in ( if Int
nToDestroy Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0
       then EnumMap CStore ItemBag
bagsToLose1  -- avoid vacuus @UpdDestroyItem@
       else let kit2 :: ItemQuant
kit2 = (Int
nToDestroy, Int -> [ItemTimer] -> [ItemTimer]
forall a. Int -> [a] -> [a]
take Int
nToDestroy [ItemTimer]
it)
                removedBags :: EnumMap CStore ItemBag
removedBags = CStore -> ItemBag -> EnumMap CStore ItemBag
forall k a. Enum k => k -> a -> EnumMap k a
EM.singleton CStore
store (ItemBag -> EnumMap CStore ItemBag)
-> ItemBag -> EnumMap CStore ItemBag
forall a b. (a -> b) -> a -> b
$ ItemId -> ItemQuant -> ItemBag
forall k a. Enum k => k -> a -> EnumMap k a
EM.singleton ItemId
iid ItemQuant
kit2
            in (ItemBag -> ItemBag -> ItemBag)
-> EnumMap CStore ItemBag
-> EnumMap CStore ItemBag
-> EnumMap CStore ItemBag
forall a k.
(a -> a -> a) -> EnumMap k a -> EnumMap k a -> EnumMap k a
EM.unionWith ((ItemQuant -> ItemQuant -> ItemQuant)
-> ItemBag -> ItemBag -> ItemBag
forall a k.
(a -> a -> a) -> EnumMap k a -> EnumMap k a -> EnumMap k a
EM.unionWith ItemQuant -> ItemQuant -> ItemQuant
mergeItemQuant)
                            EnumMap CStore ItemBag
removedBags EnumMap CStore ItemBag
bagsToLose1
     , Int
-> (CStore, (ItemId, ItemFull)) -> [(CStore, (ItemId, ItemFull))]
forall a. Int -> a -> [a]
replicate Int
nToApply (CStore
store, (ItemId
iid, ItemFull
itemFull)) [(CStore, (ItemId, ItemFull))]
-> [(CStore, (ItemId, ItemFull))] -> [(CStore, (ItemId, ItemFull))]
forall a. [a] -> [a] -> [a]
++ [(CStore, (ItemId, ItemFull))]
iidsToApply1
     , [(Bool, Int, GroupName ItemKind)]
grps2 )

sortIids :: (ItemId -> ItemFull)
         -> [(ItemId, ItemQuant)]
         -> [(ItemId, ItemQuant)]
sortIids :: (ItemId -> ItemFull)
-> [(ItemId, ItemQuant)] -> [(ItemId, ItemQuant)]
sortIids ItemId -> ItemFull
itemToF =
  -- If appearance and aspects the same, keep the order from before sort.
  let kindAndAppearance :: (ItemId, ItemQuant)
-> (Bool, ContentId ItemKind, ItemDisco, Char, Text, Flavour,
    Maybe FactionId)
kindAndAppearance (ItemId
iid, ItemQuant
_) =
        let ItemFull{itemBase :: ItemFull -> Item
itemBase=Item{Maybe FactionId
Flavour
ItemIdentity
jkind :: Item -> ItemIdentity
jfid :: Item -> Maybe FactionId
jflavour :: Item -> Flavour
jkind :: ItemIdentity
jfid :: Maybe FactionId
jflavour :: Flavour
..}, Bool
ContentId ItemKind
ItemKind
ItemDisco
itemKindId :: ItemFull -> ContentId ItemKind
itemKind :: ItemFull -> ItemKind
itemDisco :: ItemFull -> ItemDisco
itemSuspect :: ItemFull -> Bool
itemKindId :: ContentId ItemKind
itemKind :: ItemKind
itemDisco :: ItemDisco
itemSuspect :: Bool
..} = ItemId -> ItemFull
itemToF ItemId
iid
        in ( Bool -> Bool
not Bool
itemSuspect, ContentId ItemKind
itemKindId, ItemDisco
itemDisco
           , ItemKind -> Char
IK.isymbol ItemKind
itemKind, ItemKind -> Text
IK.iname ItemKind
itemKind
           , Flavour
jflavour, Maybe FactionId
jfid )
  in ((ItemId, ItemQuant)
 -> (Bool, ContentId ItemKind, ItemDisco, Char, Text, Flavour,
     Maybe FactionId))
-> [(ItemId, ItemQuant)] -> [(ItemId, ItemQuant)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (ItemId, ItemQuant)
-> (Bool, ContentId ItemKind, ItemDisco, Char, Text, Flavour,
    Maybe FactionId)
kindAndAppearance

data TileAction =
    EmbedAction (ItemId, ItemQuant)
  | ToAction (GroupName TK.TileKind)
  | WithAction [(Int, GroupName IK.ItemKind)] (GroupName TK.TileKind)
  deriving Int -> TileAction -> ShowS
[TileAction] -> ShowS
TileAction -> String
(Int -> TileAction -> ShowS)
-> (TileAction -> String)
-> ([TileAction] -> ShowS)
-> Show TileAction
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TileAction -> ShowS
showsPrec :: Int -> TileAction -> ShowS
$cshow :: TileAction -> String
show :: TileAction -> String
$cshowList :: [TileAction] -> ShowS
showList :: [TileAction] -> ShowS
Show

parseTileAction :: Bool -> Bool -> [(IK.ItemKind, (ItemId, ItemQuant))]
                -> TK.Feature
                -> Maybe TileAction
parseTileAction :: Bool
-> Bool
-> [(ItemKind, (ItemId, ItemQuant))]
-> Feature
-> Maybe TileAction
parseTileAction Bool
bproj Bool
underFeet [(ItemKind, (ItemId, ItemQuant))]
embedKindList Feature
feat = case Feature
feat of
  TK.Embed GroupName ItemKind
igroup ->
      -- Greater or equal 0 to also cover template UNKNOWN items
      -- not yet identified by the client.
    let f :: (ItemKind, (ItemId, ItemQuant)) -> Bool
f (ItemKind
itemKind, (ItemId, ItemQuant)
_) =
          Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe (-Int
1) (GroupName ItemKind -> [(GroupName ItemKind, Int)] -> Maybe Int
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup GroupName ItemKind
igroup ([(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) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
    in case ((ItemKind, (ItemId, ItemQuant)) -> Bool)
-> [(ItemKind, (ItemId, ItemQuant))]
-> Maybe (ItemKind, (ItemId, ItemQuant))
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (ItemKind, (ItemId, ItemQuant)) -> Bool
f [(ItemKind, (ItemId, ItemQuant))]
embedKindList of
      Maybe (ItemKind, (ItemId, ItemQuant))
Nothing -> Maybe TileAction
forall a. Maybe a
Nothing
      Just (ItemKind
_, (ItemId, ItemQuant)
iidkit) -> TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ (ItemId, ItemQuant) -> TileAction
EmbedAction (ItemId, ItemQuant)
iidkit
  TK.OpenTo GroupName TileKind
tgroup | Bool -> Bool
not (Bool
underFeet Bool -> Bool -> Bool
|| Bool
bproj) -> TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ GroupName TileKind -> TileAction
ToAction GroupName TileKind
tgroup
  TK.CloseTo GroupName TileKind
tgroup | Bool -> Bool
not (Bool
underFeet Bool -> Bool -> Bool
|| Bool
bproj) -> TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ GroupName TileKind -> TileAction
ToAction GroupName TileKind
tgroup
  TK.ChangeTo GroupName TileKind
tgroup | Bool -> Bool
not Bool
bproj -> TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ GroupName TileKind -> TileAction
ToAction GroupName TileKind
tgroup
  TK.OpenWith ProjectileTriggers
proj [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup | Bool -> Bool
not Bool
underFeet ->
    if ProjectileTriggers
proj ProjectileTriggers -> ProjectileTriggers -> Bool
forall a. Eq a => a -> a -> Bool
== ProjectileTriggers
TK.ProjNo Bool -> Bool -> Bool
&& Bool
bproj
    then Maybe TileAction
forall a. Maybe a
Nothing
    else TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ [(Int, GroupName ItemKind)] -> GroupName TileKind -> TileAction
WithAction [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup
  TK.CloseWith ProjectileTriggers
proj [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup | Bool -> Bool
not Bool
underFeet ->
    -- Not when standing on tile, not to autoclose doors under actor
    -- or close via dropping an item inside.
    if ProjectileTriggers
proj ProjectileTriggers -> ProjectileTriggers -> Bool
forall a. Eq a => a -> a -> Bool
== ProjectileTriggers
TK.ProjNo Bool -> Bool -> Bool
&& Bool
bproj
    then Maybe TileAction
forall a. Maybe a
Nothing
    else TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ [(Int, GroupName ItemKind)] -> GroupName TileKind -> TileAction
WithAction [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup
  TK.ChangeWith ProjectileTriggers
proj [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup ->
    if ProjectileTriggers
proj ProjectileTriggers -> ProjectileTriggers -> Bool
forall a. Eq a => a -> a -> Bool
== ProjectileTriggers
TK.ProjNo Bool -> Bool -> Bool
&& Bool
bproj
    then Maybe TileAction
forall a. Maybe a
Nothing
    else TileAction -> Maybe TileAction
forall a. a -> Maybe a
Just (TileAction -> Maybe TileAction) -> TileAction -> Maybe TileAction
forall a b. (a -> b) -> a -> b
$ [(Int, GroupName ItemKind)] -> GroupName TileKind -> TileAction
WithAction [(Int, GroupName ItemKind)]
grps GroupName TileKind
tgroup
  Feature
_ -> Maybe TileAction
forall a. Maybe a
Nothing