module Game.LambdaHack.Common.ItemDescription
( partItemN, partItem, partItemWs, partItemAW, 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 Game.LambdaHack.Common.ActorState
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 :: Bool -> Int -> Container -> LevelId -> Time -> ItemFull
-> (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 (MU.Text $ flav <+> genericName, "")
Just _ ->
let effTs = filter (not . T.null) $ textAllAE fullInfo c itemFull
it1 = case strengthFromEqpSlot IK.EqpSlotTimeout itemFull of
Nothing -> []
Just timeout ->
let timeoutTurns = timeDeltaScale (Delta timeTurn) timeout
f startT = timeShift startT timeoutTurns > localTime
in filter f (itemTimer itemFull)
len = length it1
timer = if len == 0
then ""
else if itemK itemFull == 1 && len == 1
then "(charging)"
else "(" <> tshow len <+> "charging" <> ")"
ts = take n effTs
++ (if length effTs > n then ["(...)"] else [])
++ [timer]
in (MU.Text genericName, MU.Phrase $ map MU.Text ts)
partItem :: Container -> LevelId -> Time -> ItemFull -> (MU.Part, MU.Part)
partItem = partItemN False 4
textAllAE :: Bool -> Container -> ItemFull -> [Text]
textAllAE fullInfo c ItemFull{itemBase, itemDisco} =
let features | fullInfo = 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
hurtEffect :: IK.Effect -> Bool
hurtEffect (IK.Hurt _) = True
hurtEffect _ = False
notDetail :: IK.Effect -> Bool
notDetail IK.Explode{} = fullInfo
notDetail _ = True
cstore = storeFromC c
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
restAs = sort aspects
(hurtEs, restEs) = partition hurtEffect $ sort
$ filter notDetail effects
aes = map ppE hurtEs
++ 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
_ | 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 <> ")"
in [periodicOrTimeout] ++ aes ++ [onSmash | fullInfo]
aets = case itemAE of
Just ItemAspectEffect{jaspects, jeffects} ->
splitAE tshow
jaspects aspectToSuffix
jeffects effectToSuffix
Nothing ->
splitAE (\d -> maybe "?" tshow $ Dice.reduceDice d)
(IK.iaspects itemKind) kindAspectToSuffix
(IK.ieffects itemKind) kindEffectToSuffix
in aets ++ features
partItemWs :: Int -> Container -> LevelId -> Time -> ItemFull -> MU.Part
partItemWs count c lid localTime itemFull =
let (name, stats) = partItem c lid localTime itemFull
in MU.Phrase [MU.CarWs count name, stats]
partItemAW :: Container -> LevelId -> Time -> ItemFull -> MU.Part
partItemAW c lid localTime itemFull =
let (name, stats) = partItem c lid localTime itemFull
in MU.AW $ MU.Phrase [name, stats]
partItemWownW :: MU.Part -> Container -> LevelId -> Time -> ItemFull -> MU.Part
partItemWownW partA c lid localTime itemFull =
let (name, stats) = partItem c lid localTime itemFull
in MU.WownW partA $ MU.Phrase [name, stats]
itemDesc :: Container -> LevelId -> Time -> ItemFull -> Overlay
itemDesc c lid localTime itemFull =
let (name, stats) = partItemN True 99 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 (tshow weight, "g")
ln = abs $ fromEnum $ jlid (itemBase itemFull)
colorSymbol = uncurry (flip Color.AttrChar) (viewItem $ itemBase itemFull)
f color = Color.AttrChar Color.defAttr color
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} )