module Game.LambdaHack.Common.ItemDescription
( partItemN, partItem, partItemWs, partItemAW, partItemMediumAW, partItemWownW
, itemDesc, textAllAE, viewItem
) where
import Data.List
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
import qualified NLP.Miniutter.English as MU
import qualified Game.LambdaHack.Common.Color as Color
import qualified Game.LambdaHack.Common.Dice as Dice
import Game.LambdaHack.Common.EffectDescription
import Game.LambdaHack.Common.Flavour
import Game.LambdaHack.Common.Item
import Game.LambdaHack.Common.ItemStrongest
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Common.Msg
import Game.LambdaHack.Common.Time
import qualified Game.LambdaHack.Content.ItemKind as IK
partItemN :: Int -> Int -> CStore -> LevelId -> Time -> ItemFull
-> (Bool, MU.Part, MU.Part)
partItemN fullInfo n c _lid localTime itemFull =
let genericName = jname $ itemBase itemFull
in case itemDisco itemFull of
Nothing ->
let flav = flavourToName $ jflavour $ itemBase itemFull
in (False, MU.Text $ flav <+> genericName, "")
Just iDisco ->
let (toutN, it1) = case strengthFromEqpSlot IK.EqpSlotTimeout itemFull of
Nothing -> (0, [])
Just timeout ->
let timeoutTurns = timeDeltaScale (Delta timeTurn) timeout
charging startT = timeShift startT timeoutTurns > localTime
in (timeout, filter charging (itemTimer itemFull))
len = length it1
chargingAdj | toutN == 0 = "temporary"
| otherwise = "charging"
timer | len == 0 = ""
| itemK itemFull == 1 && len == 1 = "(" <> chargingAdj <> ")"
| otherwise = "(" <> tshow len <+> chargingAdj <> ")"
skipRecharging = fullInfo <= 4 && len >= itemK itemFull
effTs = filter (not . T.null)
$ textAllAE fullInfo skipRecharging c itemFull
ts = take n effTs
++ ["(...)" | length effTs > n]
++ [timer]
isUnique aspects = IK.Unique `elem` aspects
unique = case iDisco of
ItemDisco{itemAE=Just ItemAspectEffect{jaspects}} ->
isUnique jaspects
ItemDisco{itemKind} ->
isUnique $ IK.iaspects itemKind
capName = if unique
then MU.Capitalize $ MU.Text genericName
else MU.Text genericName
in (unique, capName, MU.Phrase $ map MU.Text ts)
partItem :: CStore -> LevelId -> Time -> ItemFull -> (Bool, MU.Part, MU.Part)
partItem = partItemN 5 4
textAllAE :: Int -> Bool -> CStore -> ItemFull -> [Text]
textAllAE fullInfo skipRecharging cstore ItemFull{itemBase, itemDisco} =
let features | fullInfo >= 9 = map featureToSuff $ sort $ jfeature itemBase
| otherwise = []
in case itemDisco of
Nothing -> features
Just ItemDisco{itemKind, itemAE} ->
let periodicAspect :: IK.Aspect a -> Bool
periodicAspect IK.Periodic = True
periodicAspect _ = False
timeoutAspect :: IK.Aspect a -> Bool
timeoutAspect IK.Timeout{} = True
timeoutAspect _ = False
noEffect :: IK.Effect -> Bool
noEffect IK.NoEffect{} = True
noEffect _ = False
hurtEffect :: IK.Effect -> Bool
hurtEffect (IK.Hurt _) = True
hurtEffect (IK.Burn _) = True
hurtEffect _ = False
notDetail :: IK.Effect -> Bool
notDetail IK.Explode{} = fullInfo >= 6
notDetail _ = True
active = cstore `elem` [CEqp, COrgan]
|| cstore == CGround && isJust (strengthEqpSlot itemBase)
splitAE :: (Num a, Show a, Ord a)
=> (a -> Text)
-> [IK.Aspect a] -> (IK.Aspect a -> Text)
-> [IK.Effect] -> (IK.Effect -> Text)
-> [Text]
splitAE reduce_a aspects ppA effects ppE =
let mperiodic = find periodicAspect aspects
mtimeout = find timeoutAspect aspects
mnoEffect = find noEffect effects
restAs = sort aspects
(hurtEs, restEs) = partition hurtEffect $ sort
$ filter notDetail effects
aes = if active
then map ppA restAs ++ map ppE restEs
else map ppE restEs ++ map ppA restAs
rechargingTs = T.intercalate (T.singleton ' ')
$ filter (not . T.null)
$ map ppE $ stripRecharging restEs
onSmashTs = T.intercalate (T.singleton ' ')
$ filter (not . T.null)
$ map ppE $ stripOnSmash restEs
durable = IK.Durable `elem` jfeature itemBase
periodicOrTimeout = case mperiodic of
_ | skipRecharging || T.null rechargingTs -> ""
Just IK.Periodic ->
case mtimeout of
Just (IK.Timeout 0) | not durable ->
"(each turn until gone:"
<+> rechargingTs <> ")"
Just (IK.Timeout t) ->
"(every" <+> reduce_a t <> ":"
<+> rechargingTs <> ")"
_ -> ""
_ -> case mtimeout of
Just (IK.Timeout t) ->
"(timeout" <+> reduce_a t <> ":"
<+> rechargingTs <> ")"
_ -> ""
onSmash = if T.null onSmashTs then ""
else "(on smash:" <+> onSmashTs <> ")"
noEff = case mnoEffect of
Just (IK.NoEffect t) -> [t]
_ -> []
in noEff ++ if fullInfo >= 5 || fullInfo >= 2 && null noEff
then [periodicOrTimeout] ++ map ppE hurtEs ++ aes
++ [onSmash | fullInfo >= 7]
else map ppE hurtEs
aets = case itemAE of
Just ItemAspectEffect{jaspects, jeffects} ->
splitAE tshow
jaspects aspectToSuffix
jeffects effectToSuffix
Nothing ->
splitAE (maybe "?" tshow . Dice.reduceDice)
(IK.iaspects itemKind) kindAspectToSuffix
(IK.ieffects itemKind) kindEffectToSuffix
in aets ++ features
partItemWs :: Int -> CStore -> LevelId -> Time -> ItemFull -> MU.Part
partItemWs count c lid localTime itemFull =
let (unique, name, stats) = partItem c lid localTime itemFull
in if unique && count == 1
then MU.Phrase ["the", name, stats]
else MU.Phrase [MU.CarWs count name, stats]
partItemAW :: CStore -> LevelId -> Time -> ItemFull -> MU.Part
partItemAW c lid localTime itemFull =
let (unique, name, stats) = partItemN 4 4 c lid localTime itemFull
in if unique
then MU.Phrase ["the", name, stats]
else MU.AW $ MU.Phrase [name, stats]
partItemMediumAW :: CStore -> LevelId -> Time -> ItemFull -> MU.Part
partItemMediumAW c lid localTime itemFull =
let (unique, name, stats) = partItemN 5 100 c lid localTime itemFull
in if unique
then MU.Phrase ["the", name, stats]
else MU.AW $ MU.Phrase [name, stats]
partItemWownW :: MU.Part -> CStore -> LevelId -> Time -> ItemFull -> MU.Part
partItemWownW partA c lid localTime itemFull =
let (_, name, stats) = partItemN 4 4 c lid localTime itemFull
in MU.WownW partA $ MU.Phrase [name, stats]
itemDesc :: CStore -> LevelId -> Time -> ItemFull -> Overlay
itemDesc c lid localTime itemFull =
let (_, name, stats) = partItemN 10 100 c lid localTime itemFull
nstats = makePhrase [name, stats]
desc = case itemDisco itemFull of
Nothing -> "This item is as unremarkable as can be."
Just ItemDisco{itemKind} -> IK.idesc itemKind
weight = jweight (itemBase itemFull)
(scaledWeight, unitWeight) =
if weight > 1000
then (tshow $ fromIntegral weight / (1000 :: Double), "kg")
else if weight > 0
then (tshow weight, "g")
else ("", "")
ln = abs $ fromEnum $ jlid (itemBase itemFull)
colorSymbol = uncurry (flip Color.AttrChar) (viewItem $ itemBase itemFull)
f = Color.AttrChar Color.defAttr
lxsize = fst normalLevelBound + 1
blurb =
"D"
<+> nstats
<> ":"
<+> desc
<+> makeSentence ["Weighs", MU.Text scaledWeight <> unitWeight]
<+> makeSentence ["First found on level", MU.Text $ tshow ln]
splitBlurb = splitText lxsize blurb
attrBlurb = map (map f . T.unpack) splitBlurb
in encodeOverlay $ (colorSymbol : tail (head attrBlurb)) : tail attrBlurb
viewItem :: Item -> (Char, Color.Attr)
viewItem item = ( jsymbol item
, Color.defAttr {Color.fg = flavourToColor $ jflavour item} )