-- | Descripitons of items. 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 -- | The part of speech describing the item parameterized by the number -- of effects/aspects to show.. partItemN :: Int -> Int -> CStore -> Time -> ItemFull -> (Bool, MU.Part, MU.Part) partItemN fullInfo n c 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) -- | The part of speech describing the item. partItem :: CStore -> 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 -- TODO: use kit partItemWs :: Int -> CStore -> Time -> ItemFull -> MU.Part partItemWs count c localTime itemFull = let (unique, name, stats) = partItem c localTime itemFull in if unique && count == 1 then MU.Phrase ["the", name, stats] else MU.Phrase [MU.CarWs count name, stats] partItemAW :: CStore -> Time -> ItemFull -> MU.Part partItemAW c localTime itemFull = let (unique, name, stats) = partItemN 4 4 c localTime itemFull in if unique then MU.Phrase ["the", name, stats] else MU.AW $ MU.Phrase [name, stats] partItemMediumAW :: CStore -> Time -> ItemFull -> MU.Part partItemMediumAW c localTime itemFull = let (unique, name, stats) = partItemN 5 100 c localTime itemFull in if unique then MU.Phrase ["the", name, stats] else MU.AW $ MU.Phrase [name, stats] partItemWownW :: MU.Part -> CStore -> Time -> ItemFull -> MU.Part partItemWownW partA c localTime itemFull = let (_, name, stats) = partItemN 4 4 c localTime itemFull in MU.WownW partA $ MU.Phrase [name, stats] itemDesc :: CStore -> Time -> ItemFull -> Overlay itemDesc c localTime itemFull = let (_, name, stats) = partItemN 10 100 c 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) | weight > 1000 = (tshow $ fromIntegral weight / (1000 :: Double), "kg") | weight > 0 = (tshow weight, "g") | otherwise = ("", "") ln = abs $ fromEnum $ jlid (itemBase itemFull) colorSymbol = uncurry (flip Color.AttrChar) (viewItem $ itemBase itemFull) f = Color.AttrChar Color.defAttr lxsize = fst normalLevelBound + 1 -- TODO blurb = "D" -- dummy <+> 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} )