{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving #-}
module Game.LambdaHack.Common.Item
( ItemId, Item(..), ItemIdentity(..)
, ItemKindIx, ItemDisco(..), ItemFull(..), ItemFullKit
, DiscoveryKind, DiscoveryAspect, ItemIxMap, Benefit(..), DiscoveryBenefit
, ItemTimer, ItemQuant, ItemBag, ItemDict
, itemToFull6, aspectRecordFull
, strongestSlot, hasCharge, strongestMelee, unknownMeleeBonus, tmpMeleeBonus
#ifdef EXPOSE_INTERNAL
, unknownAspect
#endif
) where
import Prelude ()
import Game.LambdaHack.Common.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 qualified Data.Ord as Ord
import GHC.Generics (Generic)
import qualified Game.LambdaHack.Common.Dice as Dice
import Game.LambdaHack.Common.Flavour
import qualified Game.LambdaHack.Common.ItemAspect as IA
import Game.LambdaHack.Common.Kind
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Common.Time
import qualified Game.LambdaHack.Content.ItemKind as IK
newtype ItemId = ItemId Int
deriving (Show, Eq, Ord, Enum, Binary)
data Item = Item
{ jkind :: ItemIdentity
, jlid :: LevelId
, jfid :: Maybe FactionId
, jflavour :: Flavour
}
deriving (Show, Eq, Generic)
instance Hashable Item
instance Binary Item
data ItemIdentity =
IdentityObvious (ContentId IK.ItemKind)
| IdentityCovered ItemKindIx (ContentId IK.ItemKind)
deriving (Show, Eq, Generic)
instance Hashable ItemIdentity
instance Binary ItemIdentity
type DiscoveryAspect = EM.EnumMap ItemId IA.AspectRecord
newtype ItemKindIx = ItemKindIx Word16
deriving (Show, Eq, Ord, Enum, Ix.Ix, Hashable, Binary)
data ItemDisco =
ItemDiscoMean IA.KindMean
| ItemDiscoFull {itemAspect :: IA.AspectRecord}
deriving (Show, Eq, Ord)
data ItemFull = ItemFull
{ itemBase :: Item
, itemKindId :: ContentId IK.ItemKind
, itemKind :: IK.ItemKind
, itemDisco :: ItemDisco
, itemSuspect :: Bool
}
deriving Show
type ItemFullKit = (ItemFull, ItemQuant)
type DiscoveryKind = EM.EnumMap ItemKindIx (ContentId IK.ItemKind)
type ItemIxMap = EM.EnumMap ItemKindIx (ES.EnumSet ItemId)
data Benefit = Benefit
{ benInEqp :: ~Bool
, benPickup :: ~Double
, benApply :: ~Double
, benMelee :: ~Double
, benFling :: ~Double
}
deriving (Show, Generic)
instance Binary Benefit
type DiscoveryBenefit = EM.EnumMap ItemId Benefit
type ItemTimer = [Time]
type ItemQuant = (Int, ItemTimer)
type ItemBag = EM.EnumMap ItemId ItemQuant
type ItemDict = EM.EnumMap ItemId Item
itemToFull6 :: COps -> DiscoveryKind -> DiscoveryAspect -> ItemId -> Item
-> ItemFull
itemToFull6 COps{coitem, coItemSpeedup} discoKind discoAspect iid itemBase =
let (itemKindId, itemSuspect) = case jkind itemBase of
IdentityObvious ik -> (ik, False)
IdentityCovered ix ik ->
maybe (ik, True) (\ki -> (ki, False)) $ ix `EM.lookup` discoKind
itemKind = okind coitem itemKindId
km = IK.getKindMean itemKindId coItemSpeedup
itemAspectMean | itemSuspect = km {IA.kmConst = False}
| otherwise = km
itemDisco = case EM.lookup iid discoAspect of
Just itemAspect -> ItemDiscoFull itemAspect
Nothing -> ItemDiscoMean itemAspectMean
in ItemFull {..}
aspectRecordFull :: ItemFull -> IA.AspectRecord
aspectRecordFull itemFull =
case itemDisco itemFull of
ItemDiscoMean itemAspectMean -> IA.kmMean itemAspectMean
ItemDiscoFull itemAspect -> itemAspect
strongestSlot :: DiscoveryBenefit -> IA.EqpSlot -> [(ItemId, ItemFullKit)]
-> [(Int, (ItemId, ItemFullKit))]
strongestSlot discoBenefit eqpSlot is =
let f (iid, (itemFull, kit)) =
let Benefit{benInEqp, benPickup} = discoBenefit EM.! iid
in if not benInEqp
then Nothing
else Just $
let ben = if eqpSlot == IA.EqpSlotWeapon
then ceiling benPickup
else IA.prEqpSlot eqpSlot $ aspectRecordFull itemFull
in (ben, (iid, (itemFull, kit)))
in sortBy (flip $ Ord.comparing fst) $ mapMaybe f is
hasCharge :: Time -> ItemFull -> ItemQuant -> Bool
hasCharge localTime itemFull (itemK, itemTimer) =
let timeout = IA.aTimeout $ aspectRecordFull itemFull
timeoutTurns = timeDeltaScale (Delta timeTurn) timeout
charging startT = timeShift startT timeoutTurns > localTime
it1 = filter charging itemTimer
in length it1 < itemK
strongestMelee :: Maybe DiscoveryBenefit -> Time
-> [(ItemId, ItemFullKit)]
-> [(Double, (ItemId, ItemFullKit))]
strongestMelee _ _ [] = []
strongestMelee mdiscoBenefit localTime kitAss =
let f (iid, (itemFull, kit)) =
let rawDmg = (IK.damageUsefulness
$ itemKind itemFull, (iid, (itemFull, kit)))
knownOrConstantAspects = case itemDisco itemFull of
ItemDiscoMean IA.KindMean{kmConst} -> kmConst
ItemDiscoFull{} -> True
unIDedBonus | knownOrConstantAspects = 0
| otherwise = 1000
in case mdiscoBenefit of
Just discoBenefit ->
let Benefit{benMelee} = discoBenefit EM.! iid
dmg = if hasCharge localTime itemFull kit
then (- benMelee, (iid, (itemFull, kit)))
else rawDmg
in first (+ unIDedBonus) dmg
Nothing -> rawDmg
in sortBy (flip $ Ord.comparing fst) $ map f kitAss
unknownAspect :: (IA.Aspect -> [Dice.Dice]) -> ItemFull -> Bool
unknownAspect f ItemFull{itemKind=IK.ItemKind{iaspects}, ..} =
case itemDisco of
ItemDiscoMean IA.KindMean{kmConst} ->
let unknown x = let (minD, maxD) = Dice.minmaxDice x
in minD /= maxD
in itemSuspect || not kmConst && or (concatMap (map unknown . f) iaspects)
ItemDiscoFull{} -> False
unknownMeleeBonus :: [ItemFull] -> Bool
unknownMeleeBonus =
let p (IA.AddHurtMelee k) = [k]
p _ = []
f itemFull b = b || unknownAspect p itemFull
in foldr f False
tmpMeleeBonus :: [ItemFullKit] -> Int
tmpMeleeBonus kitAss =
let f (itemFull, (itemK, _)) k =
itemK * IA.aHurtMelee (aspectRecordFull itemFull) + k
in foldr f 0 $ filter (IK.isTmpCondition . itemKind . fst) kitAss