    -- | Descriptions of items.
    module Game.LambdaHack.Client.UI.ItemDescription
      ( partItem, partItemShort, partItemShortest, partItemHigh
      , partItemWsDetail, partItemWs, partItemWsShortest, partItemWsShort
      , partItemWsLong, partItemWsRanged
      , partItemShortAW, partItemMediumAW, partItemShortWownW
      , viewItem, viewItemBenefitColored, itemDesc
    #ifdef EXPOSE_INTERNAL
        -- * Internal operations
      , partItemN, textAllPowers
    #endif
      ) where

    import Prelude ()

    import Game.LambdaHack.Core.Prelude

    import           Data.Char (isAlpha, isAlphaNum)
    import qualified Data.EnumMap.Strict as EM
    import           Data.Int (Int64)
    import qualified Data.Text as T
    import qualified NLP.Miniutter.English as MU

    import           Game.LambdaHack.Client.UI.EffectDescription
    import           Game.LambdaHack.Client.UI.Overlay
    import           Game.LambdaHack.Common.Faction
    import           Game.LambdaHack.Common.Item
    import qualified Game.LambdaHack.Common.ItemAspect as IA
    import           Game.LambdaHack.Common.Misc
    import           Game.LambdaHack.Common.Time
    import           Game.LambdaHack.Common.Types
    import qualified Game.LambdaHack.Content.ItemKind as IK
    import qualified Game.LambdaHack.Core.Dice as Dice
    import qualified Game.LambdaHack.Definition.Ability as Ability
    import qualified Game.LambdaHack.Definition.Color as Color
    import           Game.LambdaHack.Definition.Defs
    import           Game.LambdaHack.Definition.Flavour

    partItemN :: Int -> FactionId -> FactionDict -> Bool -> DetailLevel -> Int
              -> Time -> ItemFull -> ItemQuant
              -> (MU.Part, MU.Part)
    partItemN :: Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    ranged DetailLevel
    detailLevel Int
    maxWordsToShow Time
    localTime
              ItemFull
    itemFull ItemQuant
    kit =
      let ([Text]
    _, Part
    r2, Part
    r3) =
            Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> ([Text], Part, Part)
    partItemN3 Int
    width FactionId
    side FactionDict
    factionD Bool
    ranged DetailLevel
    detailLevel Int
    maxWordsToShow
                       Time
    localTime ItemFull
    itemFull ItemQuant
    kit
      in (Part
    r2, Part
    r3)

    -- | The part of speech describing the item parameterized by the number
    -- of effects/aspects to show.
    partItemN3 :: Int -> FactionId -> FactionDict -> Bool -> DetailLevel -> Int
               -> Time -> ItemFull -> ItemQuant
               -> ([Text], MU.Part, MU.Part)
    partItemN3 :: Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> ([Text], Part, Part)
    partItemN3 Int
    width FactionId
    side FactionDict
    factionD Bool
    ranged DetailLevel
    detailLevel Int
    maxWordsToShow Time
    localTime
               itemFull :: ItemFull
    itemFull@ItemFull{Item
    itemBase :: Item
    itemBase :: ItemFull -> Item
    itemBase, ItemKind
    itemKind :: ItemKind
    itemKind :: ItemFull -> ItemKind
    itemKind, Bool
    itemSuspect :: Bool
    itemSuspect :: ItemFull -> Bool
    itemSuspect}
               (Int
    itemK, ItemTimers
    itemTimers) =
      let flav :: Text
    flav = Flavour -> Text
    flavourToName (Flavour -> Text) -> Flavour -> Text
    forall a b. (a -> b) -> a -> b
    $ Item -> Flavour
    jflavour Item
    itemBase
          arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
          timeout :: Int
    timeout = AspectRecord -> Int
    IA.aTimeout AspectRecord
    arItem
          temporary :: Bool
    temporary = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Fragile AspectRecord
    arItem
                      Bool -> Bool -> Bool
    && Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Periodic AspectRecord
    arItem
          ncha :: Int
    ncha = Time -> ItemQuant -> Int
    ncharges Time
    localTime (Int
    itemK, ItemTimers
    itemTimers)
          charges :: Text
    charges | Bool
    temporary = case ItemTimers
    itemTimers of
                      [] -> if Int
    itemK Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    ncha
                            then Text
    ""
                            else String -> Text
    forall a. HasCallStack => String -> a
    error (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String
    "partItemN3: charges with null timer"
                                         String -> (FactionId, ItemFull, Int, ItemTimers) -> String
    forall v. Show v => String -> v -> String
    `showFailure`
                                         (FactionId
    side, ItemFull
    itemFull, Int
    itemK, ItemTimers
    itemTimers)
                      ItemTimer
    t : ItemTimers
    _ -> if Int
    itemK Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    ncha
                               then Text
    "(ready to expire)"
                               else let total :: Delta Time
    total = Time -> ItemTimer -> Delta Time
    deltaOfItemTimer Time
    localTime ItemTimer
    t
                                    in Text
    "for" Text -> Text -> Text
    <+> Delta Time -> Text
    timeDeltaInSecondsText Delta Time
    total
                  | Int
    itemK Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    ncha = Text
    ""
                  | Int
    itemK Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 Bool -> Bool -> Bool
    && Int
    ncha Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 = Text
    "(charging)"
                  | Int
    ncha Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 = Text
    "(all charging)"
                  | Bool
    otherwise = Text
    "(" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text
    forall a. Show a => a -> Text
    tshow (Int
    itemK Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    ncha) Text -> Text -> Text
    <+> Text
    "charging)"
          skipRecharging :: Bool
    skipRecharging = DetailLevel
    detailLevel DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    <= DetailLevel
    DetailLow Bool -> Bool -> Bool
    && Int
    ncha Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0
          ([Text]
    orTs, [Text]
    powerTs, [Text]
    rangedDamage) =
            Int -> DetailLevel -> Bool -> ItemFull -> ([Text], [Text], [Text])
    textAllPowers Int
    width DetailLevel
    detailLevel Bool
    skipRecharging ItemFull
    itemFull
          lsource :: [Text]
    lsource = case Item -> Maybe FactionId
    jfid Item
    itemBase of
            Just FactionId
    fid | ItemKind -> Text
    IK.iname ItemKind
    itemKind Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    == Text
    "impressed" ->
              [Text
    "by" Text -> Text -> Text
    <+> if FactionId
    fid FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId
    side
                        then Text
    "us"
                        else Faction -> Text
    gname (FactionDict
    factionD FactionDict -> FactionId -> Faction
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! FactionId
    fid)]
            Maybe FactionId
    _ -> []
          powerTsBeginsWithAlphaOrNum :: Bool
    powerTsBeginsWithAlphaOrNum = case (Text -> String) -> [Text] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> String
    T.unpack [Text]
    powerTs of
            (Char
    c : String
    _) : [String]
    _ -> Char -> Bool
    isAlpha Char
    c Bool -> Bool -> Bool
    || Char -> Bool
    isAlphaNum Char
    c
            [String]
    _ -> Bool
    False
          -- Ranged damage displayed even if lack of space, to prevent confusion
          -- and ... when only ranged damage is missing from the description.
          displayPowers :: Bool
    displayPowers = Int
    maxWordsToShow Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1
                          Bool -> Bool -> Bool
    || Bool
    powerTsBeginsWithAlphaOrNum Bool -> Bool -> Bool
    && [Text] -> Int
    forall a. [a] -> Int
    length [Text]
    powerTs Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1
          ts :: [Text]
    ts = [Text]
    lsource
               [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if Bool
    displayPowers
                   then Int -> [Text] -> [Text]
    forall a. Int -> [a] -> [a]
    take Int
    maxWordsToShow [Text]
    powerTs
                   else [])
               [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text
    "(...)" | Bool
    displayPowers Bool -> Bool -> Bool
    && [Text] -> Int
    forall a. [a] -> Int
    length [Text]
    powerTs Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    maxWordsToShow]
               [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if Bool
    displayPowers Bool -> Bool -> Bool
    && Bool
    ranged then [Text]
    rangedDamage else [])
               [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text
    charges | Int
    maxWordsToShow Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1]
          name :: Text
    name | Bool
    temporary =
                 let adj :: Text
    adj = if Int
    timeout Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 then Text
    "temporarily" else Text
    "impermanent"
                 in Text
    adj Text -> Text -> Text
    <+> ItemKind -> Text
    IK.iname ItemKind
    itemKind
               | Bool
    itemSuspect = Text
    flav Text -> Text -> Text
    <+> ItemKind -> Text
    IK.iname ItemKind
    itemKind
               | Bool
    otherwise = ItemKind -> Text
    IK.iname ItemKind
    itemKind
      in ([Text]
    orTs, Text -> Part
    MU.Text Text
    name, if Bool
    displayPowers
                              then [Part] -> Part
    MU.Phrase ([Part] -> Part) -> [Part] -> Part
    forall a b. (a -> b) -> a -> b
    $ (Text -> Part) -> [Text] -> [Part]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Part
    MU.Text [Text]
    ts
                              else Text -> Part
    MU.Text (Text -> Part) -> Text -> Part
    forall a b. (a -> b) -> a -> b
    $ AspectRecord -> Text
    IA.aELabel AspectRecord
    arItem)

    -- TODO: simplify the code a lot
    textAllPowers :: Int -> DetailLevel -> Bool -> ItemFull
                  -> ([Text], [Text], [Text])
    textAllPowers :: Int -> DetailLevel -> Bool -> ItemFull -> ([Text], [Text], [Text])
    textAllPowers Int
    width DetailLevel
    detailLevel Bool
    skipRecharging
                  itemFull :: ItemFull
    itemFull@ItemFull{ItemKind
    itemKind :: ItemFull -> ItemKind
    itemKind :: ItemKind
    itemKind, ItemDisco
    itemDisco :: ItemDisco
    itemDisco :: ItemFull -> ItemDisco
    itemDisco} =
      let arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
          -- To handle both the cases of item identified and not, we represent
          -- aspects as a list with dice, not a record of integers as in @arItem@.
          -- If item fully known, the dice will be trivial and will display
          -- the same as integers would, so nothing is lost.
          -- If item not known fully and timeouts or any crucial flags
          -- are under @Odds@, they are ignored, so they should be avoided
          -- under @Odds@ in not fully-identified items.
          aspectsFull :: [Aspect]
    aspectsFull = case ItemDisco
    itemDisco of
            ItemDiscoMean IA.KindMean{Bool
    AspectRecord
    kmConst :: Bool
    kmMean :: AspectRecord
    kmMean :: KindMean -> AspectRecord
    kmConst :: KindMean -> Bool
    ..} | Bool
    kmConst ->
              AspectRecord -> [Aspect]
    IA.aspectRecordToList AspectRecord
    kmMean  -- exact and collated
            ItemDiscoMean{} -> ItemKind -> [Aspect]
    IK.iaspects ItemKind
    itemKind
              -- doesn't completely lose the @Odds@ case, so better than
              -- the above, even if does not collate multiple skill bonuses
            ItemDiscoFull AspectRecord
    iAspect -> AspectRecord -> [Aspect]
    IA.aspectRecordToList AspectRecord
    iAspect
          mtimeout :: Maybe Aspect
    mtimeout = (Aspect -> Bool) -> [Aspect] -> Maybe Aspect
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    find Aspect -> Bool
    IK.timeoutAspect [Aspect]
    aspectsFull
          elab :: Text
    elab = AspectRecord -> Text
    IA.aELabel AspectRecord
    arItem
          periodic :: Bool
    periodic = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Periodic AspectRecord
    arItem
          hurtMeleeAspect :: IK.Aspect -> Bool
          hurtMeleeAspect :: Aspect -> Bool
    hurtMeleeAspect (IK.AddSkill Skill
    Ability.SkHurtMelee Dice
    _) = Bool
    True
          hurtMeleeAspect Aspect
    _ = Bool
    False
          active :: Bool
    active = AspectRecord -> Bool
    IA.goesIntoEqp AspectRecord
    arItem
          splitA :: DetailLevel -> [IK.Aspect] -> ([Text], [Text])
          splitA :: DetailLevel -> [Aspect] -> ([Text], [Text])
    splitA DetailLevel
    detLev [Aspect]
    aspects =
            let ppA :: Aspect -> Text
    ppA = Aspect -> Text
    kindAspectToSuffix
                ppE :: Effect -> Text
    ppE = DetailLevel -> Effect -> Text
    effectToSuffix DetailLevel
    detLev
                reduce_a :: Dice -> Text
    reduce_a = Text -> (Int -> Text) -> Maybe Int -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "?" Int -> Text
    forall a. Show a => a -> Text
    tshow (Maybe Int -> Text) -> (Dice -> Maybe Int) -> Dice -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Dice -> Maybe Int
    Dice.reduceDice
                restEs :: [Effect]
    restEs | DetailLevel
    detLev DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailMedium
                         Bool -> Bool -> Bool
    || Bool -> Bool
    not (Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.MinorEffects AspectRecord
    arItem) =
                         ItemKind -> [Effect]
    IK.ieffects ItemKind
    itemKind
                       | Bool
    otherwise = []
                ([Effect]
    smashEffs, [Effect]
    noSmashEffs) = (Effect -> Bool) -> [Effect] -> ([Effect], [Effect])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition Effect -> Bool
    IK.onSmashEffect [Effect]
    restEs
                unSmash :: Effect -> Effect
    unSmash (IK.OnSmash Effect
    eff) = Effect
    eff
                unSmash Effect
    eff = Effect
    eff
                onSmashTs :: Text
    onSmashTs = Text -> [Text] -> Text
    T.intercalate Text
    " " ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null)
                            ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Effect -> Text
    ppE (Effect -> Text) -> (Effect -> Effect) -> Effect -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Effect -> Effect
    unSmash) [Effect]
    smashEffs
                unCombine :: Effect -> Effect
    unCombine (IK.OnCombine Effect
    eff) = Effect
    eff
                unCombine Effect
    eff = Effect
    eff
                ([Effect]
    combineEffsRaw, [Effect]
    noSmashCombineEffsRaw) =
                  (Effect -> Bool) -> [Effect] -> ([Effect], [Effect])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition Effect -> Bool
    IK.onCombineEffect [Effect]
    noSmashEffs
                onCombineRawTs :: Text
    onCombineRawTs = Text -> [Text] -> Text
    T.intercalate Text
    " " ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null)
                                 ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Effect -> Text
    ppE (Effect -> Text) -> (Effect -> Effect) -> Effect -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Effect -> Effect
    unCombine) [Effect]
    combineEffsRaw
                onCombineRawTsTooLarge :: Bool
    onCombineRawTsTooLarge =
                  DetailLevel
    detailLevel DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailHigh Bool -> Bool -> Bool
    && Text -> Int
    T.length Text
    onCombineRawTs Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    120
                ([Effect]
    combineEffs, [Effect]
    noSmashCombineEffs) =
                  if Bool
    onCombineRawTsTooLarge
                  then ([Effect]
    combineEffsRaw, [Effect]
    noSmashCombineEffsRaw)
                  else ([], [Effect]
    noSmashEffs)
                unOr :: Effect -> [Effect]
    unOr (IK.OrEffect Effect
    eff1 Effect
    eff2) = Effect -> [Effect]
    unOr Effect
    eff1 [Effect] -> [Effect] -> [Effect]
    forall a. [a] -> [a] -> [a]
    ++ Effect -> [Effect]
    unOr Effect
    eff2
                unOr Effect
    eff = [Effect
    eff]
                ppAnd :: Effect -> Text
    ppAnd (IK.AndEffect (IK.ConsumeItems [(Int, GroupName ItemKind)]
    tools [(Int, GroupName ItemKind)]
    raw) Effect
    eff) =
                  let (Text
    tcraft, Text
    traw, Text
    ttools) = [(Int, GroupName ItemKind)]
    -> [(Int, GroupName ItemKind)] -> Effect -> (Text, Text, Text)
    describeCrafting [(Int, GroupName ItemKind)]
    tools [(Int, GroupName ItemKind)]
    raw Effect
    eff
                  in if Text -> Int
    T.length Text
    tcraft Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Text -> Int
    T.length Text
    traw Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Text -> Int
    T.length Text
    ttools
                        Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    width Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    4
                     then Text
    tcraft Text -> Text -> Text
    <+> Text
    traw Text -> Text -> Text
    <+> Text
    ttools
                     else Text
    tcraft Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "\n---" Text -> Text -> Text
    <+> Text
    traw Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "\n---" Text -> Text -> Text
    <+> Text
    ttools
                ppAnd Effect
    eff = Effect -> Text
    ppE Effect
    eff
                ppOr :: Effect -> Text
    ppOr Effect
    eff = Text
    "*" Text -> Text -> Text
    <+> Text -> [Text] -> Text
    T.intercalate Text
    "\n* "
                                   ([Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null)
                                        ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Effect -> Text
    ppAnd ([Effect] -> [Text]) -> [Effect] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ Effect -> [Effect]
    unOr Effect
    eff)
                onCombineTs :: [Text]
    onCombineTs =
                  (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null) ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Effect -> Text
    ppOr (Effect -> Text) -> (Effect -> Effect) -> Effect -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Effect -> Effect
    unCombine) [Effect]
    combineEffs
                rechargingTs :: Text
    rechargingTs = Text -> [Text] -> Text
    T.intercalate Text
    " "
                               ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $ [Text
    damageText | ItemKind -> Dice
    IK.idamage ItemKind
    itemKind Dice -> Dice -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Dice
    0]
                                 [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null)
                                           ((Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Effect -> Text
    ppE [Effect]
    noSmashCombineEffs)
                fragile :: Bool
    fragile = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Fragile AspectRecord
    arItem
                periodicText :: Text
    periodicText =
                  if Bool
    periodic Bool -> Bool -> Bool
    && Bool -> Bool
    not Bool
    skipRecharging Bool -> Bool -> Bool
    && Bool -> Bool
    not (Text -> Bool
    T.null Text
    rechargingTs)
                  then case (Maybe Aspect
    mtimeout, Bool
    fragile) of
                         (Maybe Aspect
    Nothing, Bool
    True) ->
                           Text
    "(each turn until gone:" Text -> Text -> Text
    <+> Text
    rechargingTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                         (Maybe Aspect
    Nothing, Bool
    False) ->
                           Text
    "(each turn:" Text -> Text -> Text
    <+> Text
    rechargingTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                             -- timeout 0, so it just fires each turn and it's not
                             -- fragile, so a copy is not destroyed each turn
                         (Just (IK.Timeout Dice
    t), Bool
    True) ->
                           Text
    "(every" Text -> Text -> Text
    <+> Dice -> Text
    reduce_a Dice
    t Text -> Text -> Text
    <+> Text
    "until gone:"
                           Text -> Text -> Text
    <+> Text
    rechargingTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                         (Just (IK.Timeout Dice
    t), Bool
    False) ->
                           Text
    "(every" Text -> Text -> Text
    <+> Dice -> Text
    reduce_a Dice
    t Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ":" Text -> Text -> Text
    <+> Text
    rechargingTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                         (Maybe Aspect, Bool)
    _ -> String -> Text
    forall a. HasCallStack => String -> a
    error (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String
    "" String -> Maybe Aspect -> String
    forall v. Show v => String -> v -> String
    `showFailure` Maybe Aspect
    mtimeout
                  else Text
    ""
                ppERestEs :: [Text]
    ppERestEs = if Bool
    periodic
                            then [Text
    periodicText]
                            else (Effect -> Text) -> [Effect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Effect -> Text
    ppE [Effect]
    noSmashCombineEffs
                aes :: [Text]
    aes = if Bool
    active
                      then (Aspect -> Text) -> [Aspect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Aspect -> Text
    ppA [Aspect]
    aspects [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text]
    ppERestEs
                      else [Text]
    ppERestEs [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (Aspect -> Text) -> [Aspect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Aspect -> Text
    ppA [Aspect]
    aspects
                onSmash :: Text
    onSmash = if Text -> Bool
    T.null Text
    onSmashTs then Text
    ""
                          else Text
    "(on smash:" Text -> Text -> Text
    <+> Text
    onSmashTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                onCombine :: Text
    onCombine = if [Effect] -> Bool
    forall a. [a] -> Bool
    null [Effect]
    combineEffs Bool -> Bool -> Bool
    && Bool -> Bool
    not (Text -> Bool
    T.null Text
    onCombineRawTs)
                            then Text
    "(on combine:" Text -> Text -> Text
    <+> Text
    onCombineRawTs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                            else Text
    ""
                -- Either exact value or dice of @SkHurtMelee@ needed,
                -- never the average, so @arItem@ not consulted directly.
                -- If item not known fully and @SkHurtMelee@ under @Odds@,
                -- it's ignored.
                damageText :: Text
    damageText = case (Aspect -> Bool) -> [Aspect] -> Maybe Aspect
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    find Aspect -> Bool
    hurtMeleeAspect [Aspect]
    aspects of
                  Just (IK.AddSkill Skill
    Ability.SkHurtMelee Dice
    hurtMelee) ->
                    (if ItemKind -> Dice
    IK.idamage ItemKind
    itemKind Dice -> Dice -> Bool
    forall a. Eq a => a -> a -> Bool
    == Dice
    0
                     then Text
    "0d0"
                     else Dice -> Text
    forall a. Show a => a -> Text
    tshow (ItemKind -> Dice
    IK.idamage ItemKind
    itemKind))
                    Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Dice -> Text
    affixDice Dice
    hurtMelee Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "%"
                  Maybe Aspect
    _ -> if ItemKind -> Dice
    IK.idamage ItemKind
    itemKind Dice -> Dice -> Bool
    forall a. Eq a => a -> a -> Bool
    == Dice
    0
                       then Text
    ""
                       else Dice -> Text
    forall a. Show a => a -> Text
    tshow (ItemKind -> Dice
    IK.idamage ItemKind
    itemKind)
                timeoutText :: Text
    timeoutText = case Maybe Aspect
    mtimeout of
                  Maybe Aspect
    Nothing -> Text
    ""
                  Just (IK.Timeout Dice
    t) -> Text
    "(cooldown" Text -> Text -> Text
    <+> Dice -> Text
    reduce_a Dice
    t Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                                           -- timeout is called "cooldown" in UI
                  Maybe Aspect
    _ -> String -> Text
    forall a. HasCallStack => String -> a
    error (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String
    "" String -> Maybe Aspect -> String
    forall v. Show v => String -> v -> String
    `showFailure` Maybe Aspect
    mtimeout
           in ( [Text]
    onCombineTs
              , [Text
    damageText]
                [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text
    timeoutText | DetailLevel
    detLev DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    > DetailLevel
    DetailLow Bool -> Bool -> Bool
    && Bool -> Bool
    not Bool
    periodic]
                [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text]
    aes
                [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ if DetailLevel
    detLev DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailHigh
                   then [Text
    onCombine, Text
    onSmash]
                   else [Text
    onCombineRawTs] )
          hurtMult :: Int
    hurtMult = Bool -> Skills -> Skills -> Int
    armorHurtCalculation Bool
    True (AspectRecord -> Skills
    IA.aSkills AspectRecord
    arItem)
                                               Skills
    Ability.zeroSkills
          dmg :: Double
    dmg = Dice -> Double
    Dice.meanDice (Dice -> Double) -> Dice -> Double
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> Dice
    IK.idamage ItemKind
    itemKind
          rawDeltaHP :: Int64
    rawDeltaHP = Double -> Int64
    forall b. Integral b => Double -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    ceiling (Double -> Int64) -> Double -> Int64
    forall a b. (a -> b) -> a -> b
    $ Int -> Double
    intToDouble Int
    hurtMult Double -> Double -> Double
    forall a. Num a => a -> a -> a
    * Double -> Double
    xD Double
    dmg Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    100
          IK.ThrowMod{Int
    throwVelocity :: Int
    throwVelocity :: ThrowMod -> Int
    IK.throwVelocity} = AspectRecord -> ThrowMod
    IA.aToThrow AspectRecord
    arItem
          speed :: Speed
    speed = Int -> Int -> Speed
    speedFromWeight (ItemKind -> Int
    IK.iweight ItemKind
    itemKind) Int
    throwVelocity
          pdeltaHP :: Int64
    pdeltaHP = Int64 -> Speed -> Int64
    modifyDamageBySpeed Int64
    rawDeltaHP Speed
    speed
          rangedDamageDesc :: [Text]
    rangedDamageDesc = [ Text
    "{avg" Text -> Text -> Text
    <+> Int64 -> Text
    show64With2 Int64
    pdeltaHP Text -> Text -> Text
    <+> Text
    "ranged}"
                             | Int64
    pdeltaHP Int64 -> Int64 -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int64
    0 ]
            -- Note that avg melee damage would be too complex to display here,
            -- because in case of @MOwned@ the owner is different than leader,
            -- so the value would be different than when viewing the item.
          splitTry :: [Aspect] -> ([Text], [Text])
    splitTry [Aspect]
    ass =
            let splits :: [([Text], [Text])]
    splits = (DetailLevel -> ([Text], [Text]))
    -> [DetailLevel] -> [([Text], [Text])]
    forall a b. (a -> b) -> [a] -> [b]
    map (DetailLevel -> [Aspect] -> ([Text], [Text])
    `splitA` [Aspect]
    ass) [DetailLevel
    forall a. Bounded a => a
    minBound..DetailLevel
    forall a. Bounded a => a
    maxBound]
                splitsToTry :: [([Text], [Text])]
    splitsToTry = Int -> [([Text], [Text])] -> [([Text], [Text])]
    forall a. Int -> [a] -> [a]
    drop (DetailLevel -> Int
    forall a. Enum a => a -> Int
    fromEnum DetailLevel
    detailLevel) [([Text], [Text])]
    splits
                splitsValid :: [([Text], [Text])]
    splitsValid | Text -> Bool
    T.null Text
    elab = (([Text], [Text]) -> Bool)
    -> [([Text], [Text])] -> [([Text], [Text])]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (([Text], [Text]) -> ([Text], [Text]) -> Bool
    forall a. Eq a => a -> a -> Bool
    /= ([], [])) [([Text], [Text])]
    splitsToTry
                            | Bool
    otherwise = [([Text], [Text])]
    splitsToTry
            in case [([Text], [Text])]
    splitsValid of
              ([Text]
    onCombineTsSplit, [Text]
    tsSplit) : [([Text], [Text])]
    _ -> ([Text]
    onCombineTsSplit, [Text]
    tsSplit)
              [] -> ([], [])
          ([Text]
    onCombineTsAss, [Text]
    aspectDescs) =
            let aMain :: Aspect -> Bool
    aMain IK.AddSkill{} = Bool
    True
                aMain Aspect
    _ = Bool
    False
                ([Aspect]
    aspectsMain, [Aspect]
    aspectsAux) = (Aspect -> Bool) -> [Aspect] -> ([Aspect], [Aspect])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition Aspect -> Bool
    aMain [Aspect]
    aspectsFull
                ([Text]
    onCombineTsSplit, [Text]
    tsSplit) = [Aspect] -> ([Text], [Text])
    splitTry [Aspect]
    aspectsMain
            in ( [Text]
    onCombineTsSplit
               , (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Text
    "")
                 ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ Text
    elab
                   Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text]
    tsSplit
                   [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ if DetailLevel
    detailLevel DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailHigh
                      then (Aspect -> Text) -> [Aspect] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Aspect -> Text
    kindAspectToSuffix [Aspect]
    aspectsAux
                      else [] )
      in ([Text]
    onCombineTsAss, [Text]
    aspectDescs, [Text]
    rangedDamageDesc)

    -- | The part of speech describing the item.
    partItem :: Int -> FactionId -> FactionDict -> Time -> ItemFull -> ItemQuant
             -> (MU.Part, MU.Part)
    partItem :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItem Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    4

    partItemShort :: Int -> FactionId -> FactionDict -> Time -> ItemFull
                  -> ItemQuant
                  -> (MU.Part, MU.Part)
    partItemShort :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemShort Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    4

    partItemShortest :: Int -> FactionId -> FactionDict -> Time -> ItemFull
                     -> ItemQuant
                     -> (MU.Part, MU.Part)
    partItemShortest :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemShortest Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    1

    partItemHigh :: Int -> FactionId -> FactionDict -> Time -> ItemFull -> ItemQuant
                 -> ([Text], MU.Part, MU.Part)
    partItemHigh :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> ([Text], Part, Part)
    partItemHigh Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> ([Text], Part, Part)
    partItemN3 Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailHigh Int
    100

    -- The @count@ can be different than @itemK@ in @ItemFull@, e.g., when picking
    -- a subset of items to drop.
    partItemWsRanged :: Int -> FactionId -> FactionDict -> Bool -> DetailLevel
                     -> Int -> Int -> Time -> ItemFull -> ItemQuant
                     -> MU.Part
    partItemWsRanged :: Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    width FactionId
    side FactionDict
    factionD Bool
    ranged DetailLevel
    detail
                     Int
    maxWordsToShow Int
    count Time
    localTime ItemFull
    itemFull ItemQuant
    kit =
      let (Part
    name, Part
    powers) = Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    ranged DetailLevel
    detail
                                     Int
    maxWordsToShow Time
    localTime ItemFull
    itemFull ItemQuant
    kit
          arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
          periodic :: Bool
    periodic = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Periodic AspectRecord
    arItem
          condition :: Bool
    condition = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Condition AspectRecord
    arItem
          maxCount :: Int
    maxCount = Dice -> Int
    Dice.supDice (Dice -> Int) -> Dice -> Int
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> Dice
    IK.icount (ItemKind -> Dice) -> ItemKind -> Dice
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> ItemKind
    itemKind ItemFull
    itemFull
      in if | Bool
    condition Bool -> Bool -> Bool
    && Int
    count Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 -> [Part] -> Part
    MU.Phrase [Part
    name, Part
    powers]
            | Bool
    condition Bool -> Bool -> Bool
    && Bool -> Bool
    not Bool
    periodic Bool -> Bool -> Bool
    && Int
    maxCount Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1 ->
                let percent :: Int
    percent = Int
    100 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    count Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `divUp` Int
    maxCount
                    amount :: Text
    amount = Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    count Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "-strong"
                             Text -> Text -> Text
    <+> Text
    "(" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    percent Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "%)"
                in [Part] -> Part
    MU.Phrase [Text -> Part
    MU.Text Text
    amount, Part
    name, Part
    powers]
            | Bool
    condition ->
                [Part] -> Part
    MU.Phrase [Text -> Part
    MU.Text (Text -> Part) -> Text -> Part
    forall a b. (a -> b) -> a -> b
    $ Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    count Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "-fold", Part
    name, Part
    powers]
            | Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Unique AspectRecord
    arItem -> case Int
    count of
                Int
    0 -> [Part] -> Part
    MU.Phrase [Part
    "none of", Part
    name, Part
    powers]
                Int
    1 -> [Part] -> Part
    MU.Phrase [Part
    name, Part
    powers]
                Int
    _ -> [Part] -> Part
    MU.Phrase [Int -> Part
    MU.Car Int
    count, Part
    "of", Part -> Part
    MU.Ws Part
    name, Part
    powers]
            | Bool
    otherwise -> [Part] -> Part
    MU.Phrase [Int -> Part -> Part
    MU.CarAWs Int
    count Part
    name, Part
    powers]

    partItemWsDetail :: DetailLevel
                     -> Int -> FactionId -> FactionDict -> Int -> Time -> ItemFull
                     -> ItemQuant
                     -> MU.Part
    partItemWsDetail :: DetailLevel
    -> Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsDetail DetailLevel
    DetailLow = Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsShortest
    partItemWsDetail DetailLevel
    DetailMedium = Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWs
    partItemWsDetail DetailLevel
    DetailHigh = Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsLong
    partItemWsDetail DetailLevel
    DetailAll = Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsLong

    partItemWs :: Int -> FactionId -> FactionDict -> Int -> Time -> ItemFull
               -> ItemQuant
               -> MU.Part
    partItemWs :: Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWs Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    4

    partItemWsShortest :: Int -> FactionId -> FactionDict -> Int -> Time -> ItemFull
                       -> ItemQuant
                       -> MU.Part
    partItemWsShortest :: Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsShortest Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    1

    partItemWsShort :: Int -> FactionId -> FactionDict -> Int -> Time -> ItemFull
                    -> ItemQuant
                    -> MU.Part
    partItemWsShort :: Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsShort Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    4

    partItemWsLong :: Int -> FactionId -> FactionDict -> Int -> Time -> ItemFull
                   -> ItemQuant
                   -> MU.Part
    partItemWsLong :: Int
    -> FactionId
    -> FactionDict
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsLong Int
    width FactionId
    side FactionDict
    factionD =
      Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailMedium Int
    100

    partItemShortAW :: Int -> FactionId -> FactionDict -> Time -> ItemFull
                    -> ItemQuant
                    -> MU.Part
    partItemShortAW :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemShortAW Int
    width FactionId
    side FactionDict
    factionD Time
    localTime ItemFull
    itemFull ItemQuant
    kit =
      let (Part
    name, Part
    _) = Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemShort Int
    width FactionId
    side FactionDict
    factionD Time
    localTime ItemFull
    itemFull ItemQuant
    kit
          arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
      in if Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Unique AspectRecord
    arItem then Part
    name else Part -> Part
    MU.AW Part
    name

    partItemMediumAW :: Int -> FactionId -> FactionDict -> Time -> ItemFull
                     -> ItemQuant
                     -> MU.Part
    partItemMediumAW :: Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemMediumAW Int
    width FactionId
    side FactionDict
    factionD Time
    localTime ItemFull
    itemFull ItemQuant
    kit =
      let (Part
    name, Part
    powers) =
            Int
    -> FactionId
    -> FactionDict
    -> Bool
    -> DetailLevel
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemN Int
    width FactionId
    side FactionDict
    factionD Bool
    False DetailLevel
    DetailLow Int
    100 Time
    localTime
                      ItemFull
    itemFull ItemQuant
    kit
          arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
          phrase :: Part
    phrase = [Part] -> Part
    MU.Phrase [Part
    name, Part
    powers]
      in if Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Unique AspectRecord
    arItem then Part
    phrase else Part -> Part
    MU.AW Part
    phrase

    partItemShortWownW :: Int -> FactionId -> FactionDict -> MU.Part -> Time
                       -> ItemFull -> ItemQuant
                       -> MU.Part
    partItemShortWownW :: Int
    -> FactionId
    -> FactionDict
    -> Part
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemShortWownW Int
    width FactionId
    side FactionDict
    factionD Part
    partA Time
    localTime ItemFull
    itemFull ItemQuant
    kit =
      let (Part
    name, Part
    _) = Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> (Part, Part)
    partItemShort Int
    width FactionId
    side FactionDict
    factionD Time
    localTime ItemFull
    itemFull ItemQuant
    kit
      in Part -> Part -> Part
    MU.WownW Part
    partA Part
    name

    viewItem :: ItemFull -> Color.AttrCharW32
    {-# INLINE viewItem #-}
    viewItem :: ItemFull -> AttrCharW32
    viewItem ItemFull
    itemFull =
      Color -> Char -> AttrCharW32
    Color.attrChar2ToW32 (Flavour -> Color
    flavourToColor (Flavour -> Color) -> Flavour -> Color
    forall a b. (a -> b) -> a -> b
    $ Item -> Flavour
    jflavour (Item -> Flavour) -> Item -> Flavour
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> Item
    itemBase ItemFull
    itemFull)
                           (Char -> Char
    forall c. Char -> Char
    displayContentSymbol (Char -> Char) -> Char -> Char
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> Char
    IK.isymbol (ItemKind -> Char) -> ItemKind -> Char
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> ItemKind
    itemKind ItemFull
    itemFull)

    viewItemBenefitColored :: DiscoveryBenefit -> ItemId -> ItemFull
                           -> Color.AttrCharW32
    viewItemBenefitColored :: DiscoveryBenefit -> ItemId -> ItemFull -> AttrCharW32
    viewItemBenefitColored DiscoveryBenefit
    discoBenefit ItemId
    iid ItemFull
    itemFull =
      -- The map @discoBenefit@ is normally used by AI to tell it in what role
      -- an item can be employed. In particular, ` benInEqp` says if an item
      -- buffs stats enough (and nerfs not too much) to be worth equipping.
      -- Here it's (ab)used to tell if an item (only a status effect item
      -- in this case, marked with `Ability.Condition`) is beneficial or not
      -- and to signal that in the organs UI menu.
      let color :: Color
    color = if Benefit -> Bool
    benInEqp (DiscoveryBenefit
    discoBenefit DiscoveryBenefit -> ItemId -> Benefit
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! ItemId
    iid)
                      then Color
    Color.BrGreen
                      else Color
    Color.BrRed
      in Color -> Char -> AttrCharW32
    Color.attrChar2ToW32
           Color
    color (Char -> Char
    forall c. Char -> Char
    displayContentSymbol (Char -> Char) -> Char -> Char
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> Char
    IK.isymbol (ItemKind -> Char) -> ItemKind -> Char
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> ItemKind
    itemKind ItemFull
    itemFull)

    itemDesc :: Int -> Bool -> FactionId -> FactionDict -> Int -> ItemDialogMode
             -> Time -> LevelId -> ItemFull -> ItemQuant
             -> AttrString
    itemDesc :: Int
    -> Bool
    -> FactionId
    -> FactionDict
    -> Int
    -> ItemDialogMode
    -> Time
    -> LevelId
    -> ItemFull
    -> ItemQuant
    -> AttrString
    itemDesc Int
    width Bool
    markParagraphs FactionId
    side FactionDict
    factionD Int
    aHurtMeleeOfOwner ItemDialogMode
    dmode Time
    localTime
             LevelId
    jlid itemFull :: ItemFull
    itemFull@ItemFull{Item
    itemBase :: ItemFull -> Item
    itemBase :: Item
    itemBase, ItemKind
    itemKind :: ItemFull -> ItemKind
    itemKind :: ItemKind
    itemKind, ItemDisco
    itemDisco :: ItemFull -> ItemDisco
    itemDisco :: ItemDisco
    itemDisco, Bool
    itemSuspect :: ItemFull -> Bool
    itemSuspect :: Bool
    itemSuspect}
             ItemQuant
    kit =
      let ([Text]
    orTs, Part
    name, Part
    powers) =
            Int
    -> FactionId
    -> FactionDict
    -> Time
    -> ItemFull
    -> ItemQuant
    -> ([Text], Part, Part)
    partItemHigh Int
    width FactionId
    side FactionDict
    factionD Time
    localTime ItemFull
    itemFull ItemQuant
    kit
          arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
          npowers :: Text
    npowers = [Part] -> Text
    makePhrase [Part
    name, Part
    powers]
          IK.ThrowMod{Int
    throwVelocity :: ThrowMod -> Int
    throwVelocity :: Int
    IK.throwVelocity, Int
    throwLinger :: Int
    throwLinger :: ThrowMod -> Int
    IK.throwLinger} = AspectRecord -> ThrowMod
    IA.aToThrow AspectRecord
    arItem
          speed :: Speed
    speed = Int -> Int -> Speed
    speedFromWeight (ItemKind -> Int
    IK.iweight ItemKind
    itemKind) Int
    throwVelocity
          range :: Int
    range = Speed -> Int -> Int
    rangeFromSpeedAndLinger Speed
    speed Int
    throwLinger
          plausiblyThrown :: Bool
    plausiblyThrown =
            ItemDialogMode
    dmode ItemDialogMode -> [ItemDialogMode] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [ CStore -> ItemDialogMode
    MStore CStore
    CGround, CStore -> ItemDialogMode
    MStore CStore
    CEqp, CStore -> ItemDialogMode
    MStore CStore
    CStash
                         , ItemDialogMode
    MOwned, SLore -> ItemDialogMode
    MLore SLore
    SItem ]
          plausiblyFlies :: Bool
    plausiblyFlies = ItemDialogMode
    dmode ItemDialogMode -> ItemDialogMode -> Bool
    forall a. Eq a => a -> a -> Bool
    == SLore -> ItemDialogMode
    MLore SLore
    SBlast
          tspeed :: Text
    tspeed | Bool -> Bool
    not (Bool
    plausiblyThrown Bool -> Bool -> Bool
    || Bool
    plausiblyFlies) = Text
    ""
                 | Speed
    speed Speed -> Speed -> Bool
    forall a. Ord a => a -> a -> Bool
    < Speed
    speedLimp =
                   if Bool
    plausiblyThrown
                   then Text
    "When thrown, it drops at once."
                   else Text
    "When airborne, it drops at once."
                 | Speed
    speed Speed -> Speed -> Bool
    forall a. Ord a => a -> a -> Bool
    < Speed
    speedWalk =
                   if Bool
    plausiblyThrown
                   then Text
    "When thrown, it drops after one meter."
                   else Text
    "When airborne, it drops after one meter."
                 | Bool
    otherwise =
                   (if Bool
    plausiblyThrown
                    then Text
    "Can be thrown at"
                    else Text
    "Travels at")
                   Text -> Text -> Text
    <+> String -> Text
    T.pack (Int -> String
    displaySpeed (Int -> String) -> Int -> String
    forall a b. (a -> b) -> a -> b
    $ Speed -> Int
    fromSpeed Speed
    speed)
                   Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Int
    throwLinger Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    100
                       then let trange :: Text
    trange = if Int
    range Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0
                                         then Text
    "immediately"
                                         else Text
    "after" Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    range Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "m"
                            in Text
    " dropping" Text -> Text -> Text
    <+> Text
    trange
                                 -- comma here is logical but looks bad
                       else Text
    "")
                   Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."
          tsuspect :: [Text]
    tsuspect = [Text
    "You are unsure what it does." | Bool
    itemSuspect]
          (Text
    desc, Text
    aspectSentences, Text
    damageAnalysis) =
            let aspects :: [Aspect]
    aspects = case ItemDisco
    itemDisco of
                  ItemDiscoMean IA.KindMean{Bool
    AspectRecord
    kmMean :: KindMean -> AspectRecord
    kmConst :: KindMean -> Bool
    kmConst :: Bool
    kmMean :: AspectRecord
    ..} | Bool
    kmConst ->
                    AspectRecord -> [Aspect]
    IA.aspectRecordToList AspectRecord
    kmMean  -- exact and collated
                  ItemDiscoMean{} -> ItemKind -> [Aspect]
    IK.iaspects ItemKind
    itemKind
                    -- doesn't completely lose the @Odds@ case, so better than
                    -- the above, even if does not collate multiple skill bonuses
                  ItemDiscoFull AspectRecord
    iAspect -> AspectRecord -> [Aspect]
    IA.aspectRecordToList AspectRecord
    iAspect
                sentences :: [Text]
    sentences = [Text]
    tsuspect [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (Aspect -> Maybe Text) -> [Aspect] -> [Text]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe Aspect -> Maybe Text
    aspectToSentence [Aspect]
    aspects
                aHurtMeleeOfItem :: Int
    aHurtMeleeOfItem = Skill -> AspectRecord -> Int
    IA.getSkill Skill
    Ability.SkHurtMelee AspectRecord
    arItem
                meanDmg :: Int
    meanDmg = Double -> Int
    forall b. Integral b => Double -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    ceiling (Double -> Int) -> Double -> Int
    forall a b. (a -> b) -> a -> b
    $ Dice -> Double
    Dice.meanDice (ItemKind -> Dice
    IK.idamage ItemKind
    itemKind)
                dmgAn :: Text
    dmgAn = if Int
    meanDmg Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0 then Text
    "" else
                  let multRaw :: Int
    multRaw = Int
    aHurtMeleeOfOwner
                                Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + if ItemDialogMode
    dmode ItemDialogMode -> [ItemDialogMode] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [CStore -> ItemDialogMode
    MStore CStore
    CEqp, CStore -> ItemDialogMode
    MStore CStore
    COrgan]
                                  then Int
    0
                                  else Int
    aHurtMeleeOfItem
                      mult :: Int
    mult = Int
    100 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    min Int
    100 (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max (-Int
    95) Int
    multRaw)
                      percentDeltaHP :: Int64
    percentDeltaHP = Int -> Int64
    xM Int
    meanDmg Int64 -> Int64 -> Int64
    forall a. Integral a => a -> a -> a
    `divUp` Int64
    100
                      rawDeltaHP :: Int64
    rawDeltaHP = forall target source. From source target => source -> target
    into @Int64 Int
    mult Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    percentDeltaHP
                      pmult :: Int
    pmult = Int
    100 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    min Int
    100 (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max (-Int
    95) Int
    aHurtMeleeOfItem)
                      prawDeltaHP :: Int64
    prawDeltaHP = forall target source. From source target => source -> target
    into @Int64 Int
    pmult Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    percentDeltaHP
                      pdeltaHP :: Int64
    pdeltaHP = Int64 -> Speed -> Int64
    modifyDamageBySpeed Int64
    prawDeltaHP Speed
    speed
                      minDeltaHP :: Int64
    minDeltaHP = Int64
    5 Int64 -> Int64 -> Int64
    forall a. Num a => a -> a -> a
    * Int64
    percentDeltaHP
                      mDeltaHP :: Int64
    mDeltaHP = Int64 -> Speed -> Int64
    modifyDamageBySpeed Int64
    minDeltaHP Speed
    speed
                  in
                    Text
    "Against defenceless foes you'd inflict around"
                      -- rounding and non-id items
                    Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    meanDmg
                    Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "*" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    mult Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "%"
                    Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "=" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int64 -> Text
    show64With2 Int64
    rawDeltaHP
                    Text -> Text -> Text
    <+> Text
    "melee damage (min" Text -> Text -> Text
    <+> Int64 -> Text
    show64With2 Int64
    minDeltaHP Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                    Text -> Text -> Text
    <+> (if Int64
    pdeltaHP Int64 -> Int64 -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int64
    0 then Text
    "" else
                           Text
    "and"
                           Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    meanDmg
                           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "*" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    pmult Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "%"
                           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "*" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "speed^2"
                           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "/" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text
    forall a. Show a => a -> Text
    tshow (Speed -> Int
    fromSpeed Speed
    speedThrust
                                            Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `divUp` Int
    10) Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "^2"
                           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "=" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int64 -> Text
    show64With2 Int64
    pdeltaHP
                           Text -> Text -> Text
    <+> Text
    "ranged damage (min" Text -> Text -> Text
    <+> Int64 -> Text
    show64With2 Int64
    mDeltaHP Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")")
                    Text -> Text -> Text
    <+> Text
    "with it"
                    Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> if Dice -> Int
    Dice.infDice (ItemKind -> Dice
    IK.idamage ItemKind
    itemKind)
                          Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Dice -> Int
    Dice.supDice (ItemKind -> Dice
    IK.idamage ItemKind
    itemKind)
                       then Text
    "."
                       else Text
    "on average."
            in (ItemKind -> Text
    IK.idesc ItemKind
    itemKind, Text -> [Text] -> Text
    T.intercalate Text
    " " [Text]
    sentences, Text
    tspeed Text -> Text -> Text
    <+> Text
    dmgAn)
          weight :: Int
    weight = ItemKind -> Int
    IK.iweight ItemKind
    itemKind
          (Text
    scaledWeight, Part
    unitWeight)
            | Int
    weight Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1000 =
              (Double -> Text
    forall a. Show a => a -> Text
    tshow (Double -> Text) -> Double -> Text
    forall a b. (a -> b) -> a -> b
    $ Int -> Double
    intToDouble Int
    weight Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    1000, Part
    "kg")
            | Bool
    otherwise = (Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    weight, Part
    "g")
          onLevel :: Text
    onLevel = Text
    "on level" Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow (Int -> Int
    forall a. Num a => a -> a
    abs (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $ LevelId -> Int
    forall a. Enum a => a -> Int
    fromEnum LevelId
    jlid) Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."
          discoFirst :: Text
    discoFirst = (if Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Unique AspectRecord
    arItem
                        then Text
    "Discovered"
                        else Text
    "First seen")
                       Text -> Text -> Text
    <+> Text
    onLevel
          whose :: FactionId -> Text
    whose FactionId
    fid = Faction -> Text
    gname (FactionDict
    factionD FactionDict -> FactionId -> Faction
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! FactionId
    fid)
          sourceDesc :: Text
    sourceDesc =
            case Item -> Maybe FactionId
    jfid Item
    itemBase of
              Just FactionId
    fid | Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Condition AspectRecord
    arItem ->
                Text
    "Caused by" Text -> Text -> Text
    <+> (if FactionId
    fid FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId
    side then Text
    "us" else FactionId -> Text
    whose FactionId
    fid)
                Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ". First observed" Text -> Text -> Text
    <+> Text
    onLevel
              Just FactionId
    fid ->
                Text
    "Coming from" Text -> Text -> Text
    <+> FactionId -> Text
    whose FactionId
    fid
                Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "." Text -> Text -> Text
    <+> Text
    discoFirst
              Maybe FactionId
    _ -> Text
    discoFirst
          -- Organs are almost always either empty or more than singular,
          -- so the "organs" below is fine. Also, some organs come in pairs
          -- or more, so we don't know the number without much more work,
          -- so @squashedWWandW@ would be out of place. Also, mentioning
          -- two hands and two legs is not that enlightening and the number
          -- is not shown in organ lore, so this should wait until we add
          -- proper hyperlinks both ways instead of relying of names.
          ikitToPart :: [(GroupName c, b)] -> Part
    ikitToPart = Text -> Part
    MU.Text (Text -> Part)
    -> ([(GroupName c, b)] -> Text) -> [(GroupName c, b)] -> Part
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> [Text] -> Text
    T.intercalate Text
    ", " ([Text] -> Text)
    -> ([(GroupName c, b)] -> [Text]) -> [(GroupName c, b)] -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((GroupName c, b) -> Text) -> [(GroupName c, b)] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (GroupName c -> Text
    forall c. GroupName c -> Text
    displayGroupName (GroupName c -> Text)
    -> ((GroupName c, b) -> GroupName c) -> (GroupName c, b) -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (GroupName c, b) -> GroupName c
    forall a b. (a, b) -> a
    fst)
          ([(GroupName ItemKind, CStore)]
    ikitOrganNames, [(GroupName ItemKind, CStore)]
    ikitOtherNames) =
            ((GroupName ItemKind, CStore) -> Bool)
    -> [(GroupName ItemKind, CStore)]
    -> ([(GroupName ItemKind, CStore)], [(GroupName ItemKind, CStore)])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    partition ((CStore -> CStore -> Bool
    forall a. Eq a => a -> a -> Bool
    == CStore
    COrgan) (CStore -> Bool)
    -> ((GroupName ItemKind, CStore) -> CStore)
    -> (GroupName ItemKind, CStore)
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (GroupName ItemKind, CStore) -> CStore
    forall a b. (a, b) -> b
    snd) ([(GroupName ItemKind, CStore)]
     -> ([(GroupName ItemKind, CStore)],
         [(GroupName ItemKind, CStore)]))
    -> [(GroupName ItemKind, CStore)]
    -> ([(GroupName ItemKind, CStore)], [(GroupName ItemKind, CStore)])
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> [(GroupName ItemKind, CStore)]
    IK.ikit ItemKind
    itemKind
          ikitDesc :: Text
    ikitDesc | [(GroupName ItemKind, CStore)] -> Bool
    forall a. [a] -> Bool
    null [(GroupName ItemKind, CStore)]
    ikitOrganNames = Text
    ""
                   | Bool
    otherwise =
            [Part] -> Text
    makeSentence
              [ Part
    "the actor has organs of this kind:"
              , [(GroupName ItemKind, CStore)] -> Part
    forall {c} {b}. [(GroupName c, b)] -> Part
    ikitToPart [(GroupName ItemKind, CStore)]
    ikitOrganNames ]
            Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> if [(GroupName ItemKind, CStore)] -> Bool
    forall a. [a] -> Bool
    null [(GroupName ItemKind, CStore)]
    ikitOtherNames
               then Text
    ""
               else Text
    "\n\n"
                    Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> [Part] -> Text
    makeSentence
                         [ Part
    "the actor starts in possession of the following:"
                         , [(GroupName ItemKind, CStore)] -> Part
    forall {c} {b}. [(GroupName c, b)] -> Part
    ikitToPart [(GroupName ItemKind, CStore)]
    ikitOtherNames ]
          colorSymbol :: AttrCharW32
    colorSymbol = ItemFull -> AttrCharW32
    viewItem ItemFull
    itemFull
          blurb :: Text
    blurb =
           (((Text
    " "
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    npowers
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Bool
    markParagraphs then Text
    "\n\n" else Text
    " ")
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> [Text] -> Text
    T.intercalate Text
    "\n\n" [Text]
    orTs
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Bool
    markParagraphs Bool -> Bool -> Bool
    && Bool -> Bool
    not ([Text] -> Bool
    forall a. [a] -> Bool
    null [Text]
    orTs) then Text
    "\n\n" else Text
    "")
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    desc
              Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Bool
    markParagraphs Bool -> Bool -> Bool
    && Bool -> Bool
    not (Text -> Bool
    T.null Text
    desc) then Text
    "\n\n" else Text
    ""))
            Text -> Text -> Text
    <+> (if Int
    weight Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0
                 then [Part] -> Text
    makeSentence
                        [Part
    "Weighs around", Text -> Part
    MU.Text Text
    scaledWeight Part -> Part -> Part
    forall a. Semigroup a => a -> a -> a
    <> Part
    unitWeight]
                 else Text
    ""))
            Text -> Text -> Text
    <+> Text
    aspectSentences
            Text -> Text -> Text
    <+> Text
    sourceDesc
            Text -> Text -> Text
    <+> Text
    damageAnalysis)
           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Bool
    markParagraphs Bool -> Bool -> Bool
    && Bool -> Bool
    not (Text -> Bool
    T.null Text
    ikitDesc) then Text
    "\n\n" else Text
    "\n")
           Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ikitDesc
      in AttrCharW32
    colorSymbol AttrCharW32 -> AttrString -> AttrString
    forall a. a -> [a] -> [a]
    : Text -> AttrString
    textToAS Text
    blurb