    -- | Helper functions for both inventory management and human commands.
    module Game.LambdaHack.Client.UI.HandleHelperM
      ( FailError, showFailError, MError, mergeMError, FailOrCmd, failWith
      , failSer, failMsg, weaveJust
      , pointmanCycle, pointmanCycleLevel, partyAfterLeader
      , pickLeader, doLook, pickLeaderWithPointer
      , itemOverlay, skillsOverlay, placesFromState, placesOverlay
      , factionsFromState, factionsOverlay
      , describeMode, modesOverlay
      , pickNumber, guardItemSize, lookAtItems, lookAtStash, lookAtPosition
      , displayOneMenuItem, okxItemLoreInline, okxItemLoreMsg, itemDescOverlays
      , cycleLore, spoilsBlurb, ppContainerWownW, nxtGameMode
    #ifdef EXPOSE_INTERNAL
        -- * Internal operations
      , itemOverlayFromState, lookAtTile, lookAtActors, guardItemVerbs
    #endif
      ) where

    import Prelude ()

    import Game.LambdaHack.Core.Prelude

    import           Control.Applicative
    import qualified Data.Char as Char
    import qualified Data.EnumMap.Strict as EM
    import qualified Data.EnumSet as ES
    import qualified Data.Map.Strict as M
    import qualified Data.Text as T
    import qualified NLP.Miniutter.English as MU

    import           Game.LambdaHack.Client.CommonM
    import           Game.LambdaHack.Client.MonadClient
    import           Game.LambdaHack.Client.State
    import           Game.LambdaHack.Client.UI.ActorUI
    import           Game.LambdaHack.Client.UI.Content.Screen
    import           Game.LambdaHack.Client.UI.ContentClientUI
    import           Game.LambdaHack.Client.UI.EffectDescription
    import           Game.LambdaHack.Client.UI.Frame
    import           Game.LambdaHack.Client.UI.ItemDescription
    import qualified Game.LambdaHack.Client.UI.Key as K
    import           Game.LambdaHack.Client.UI.MonadClientUI
    import           Game.LambdaHack.Client.UI.Msg
    import           Game.LambdaHack.Client.UI.MsgM
    import           Game.LambdaHack.Client.UI.Overlay
    import           Game.LambdaHack.Client.UI.PointUI
    import           Game.LambdaHack.Client.UI.SessionUI
    import           Game.LambdaHack.Client.UI.Slideshow
    import           Game.LambdaHack.Client.UI.SlideshowM
    import           Game.LambdaHack.Common.Actor
    import           Game.LambdaHack.Common.ActorState
    import           Game.LambdaHack.Common.ClientOptions
    import           Game.LambdaHack.Common.Faction
    import qualified Game.LambdaHack.Common.HighScore as HighScore
    import           Game.LambdaHack.Common.Item
    import qualified Game.LambdaHack.Common.ItemAspect as IA
    import           Game.LambdaHack.Common.Kind
    import           Game.LambdaHack.Common.Level
    import           Game.LambdaHack.Common.Misc
    import           Game.LambdaHack.Common.MonadStateRead
    import           Game.LambdaHack.Common.Perception
    import           Game.LambdaHack.Common.Point
    import           Game.LambdaHack.Common.ReqFailure
    import           Game.LambdaHack.Common.State
    import           Game.LambdaHack.Common.Time
    import           Game.LambdaHack.Common.Types
    import qualified Game.LambdaHack.Content.FactionKind as FK
    import qualified Game.LambdaHack.Content.ItemKind as IK
    import qualified Game.LambdaHack.Content.ModeKind as MK
    import qualified Game.LambdaHack.Content.PlaceKind as PK
    import qualified Game.LambdaHack.Content.TileKind as TK
    import qualified Game.LambdaHack.Definition.Ability as Ability
    import qualified Game.LambdaHack.Definition.Color as Color
    import           Game.LambdaHack.Definition.Defs

    -- | Message describing the cause of failure of human command.
    newtype FailError = FailError {FailError -> Text
    failError :: Text}
      deriving (Int -> FailError -> ShowS
    [FailError] -> ShowS
    FailError -> String
    (Int -> FailError -> ShowS)
    -> (FailError -> String)
    -> ([FailError] -> ShowS)
    -> Show FailError
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> FailError -> ShowS
    showsPrec :: Int -> FailError -> ShowS
    $cshow :: FailError -> String
    show :: FailError -> String
    $cshowList :: [FailError] -> ShowS
    showList :: [FailError] -> ShowS
    Show, FailError -> FailError -> Bool
    (FailError -> FailError -> Bool)
    -> (FailError -> FailError -> Bool) -> Eq FailError
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: FailError -> FailError -> Bool
    == :: FailError -> FailError -> Bool
    $c/= :: FailError -> FailError -> Bool
    /= :: FailError -> FailError -> Bool
    Eq)

    showFailError :: FailError -> Text
    showFailError :: FailError -> Text
    showFailError (FailError Text
    err) = Text
    "*" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    err Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "*"

    type MError = Maybe FailError

    mergeMError :: MError -> MError -> MError
    mergeMError :: MError -> MError -> MError
    mergeMError MError
    Nothing MError
    Nothing = MError
    forall a. Maybe a
    Nothing
    mergeMError merr1 :: MError
    merr1@Just{} MError
    Nothing = MError
    merr1
    mergeMError MError
    Nothing merr2 :: MError
    merr2@Just{} = MError
    merr2
    mergeMError (Just FailError
    err1) (Just FailError
    err2) =
      FailError -> MError
    forall a. a -> Maybe a
    Just (FailError -> MError) -> FailError -> MError
    forall a b. (a -> b) -> a -> b
    $ Text -> FailError
    FailError (Text -> FailError) -> Text -> FailError
    forall a b. (a -> b) -> a -> b
    $ FailError -> Text
    failError FailError
    err1 Text -> Text -> Text
    <+> Text
    "and" Text -> Text -> Text
    <+> FailError -> Text
    failError FailError
    err2

    type FailOrCmd a = Either FailError a

    failWith :: MonadClientUI m => Text -> m (FailOrCmd a)
    failWith :: forall (m :: * -> *) a. MonadClientUI m => Text -> m (FailOrCmd a)
    failWith Text
    err = Bool -> m (FailOrCmd a) -> m (FailOrCmd a)
    forall a. HasCallStack => Bool -> a -> a
    assert (Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ Text -> Bool
    T.null Text
    err) (m (FailOrCmd a) -> m (FailOrCmd a))
    -> m (FailOrCmd a) -> m (FailOrCmd a)
    forall a b. (a -> b) -> a -> b
    $ FailOrCmd a -> m (FailOrCmd a)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (FailOrCmd a -> m (FailOrCmd a)) -> FailOrCmd a -> m (FailOrCmd a)
    forall a b. (a -> b) -> a -> b
    $ FailError -> FailOrCmd a
    forall a b. a -> Either a b
    Left (FailError -> FailOrCmd a) -> FailError -> FailOrCmd a
    forall a b. (a -> b) -> a -> b
    $ Text -> FailError
    FailError Text
    err

    failSer :: MonadClientUI m => ReqFailure -> m (FailOrCmd a)
    failSer :: forall (m :: * -> *) a.
    MonadClientUI m =>
    ReqFailure -> m (FailOrCmd a)
    failSer = Text -> m (FailOrCmd a)
    forall (m :: * -> *) a. MonadClientUI m => Text -> m (FailOrCmd a)
    failWith (Text -> m (FailOrCmd a))
    -> (ReqFailure -> Text) -> ReqFailure -> m (FailOrCmd a)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReqFailure -> Text
    showReqFailure

    failMsg :: MonadClientUI m => Text -> m MError
    failMsg :: forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg Text
    err = Bool -> m MError -> m MError
    forall a. HasCallStack => Bool -> a -> a
    assert (Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ Text -> Bool
    T.null Text
    err) (m MError -> m MError) -> m MError -> m MError
    forall a b. (a -> b) -> a -> b
    $ MError -> m MError
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (MError -> m MError) -> MError -> m MError
    forall a b. (a -> b) -> a -> b
    $ FailError -> MError
    forall a. a -> Maybe a
    Just (FailError -> MError) -> FailError -> MError
    forall a b. (a -> b) -> a -> b
    $ Text -> FailError
    FailError Text
    err

    weaveJust :: FailOrCmd a -> Either MError a
    weaveJust :: forall a. FailOrCmd a -> Either MError a
    weaveJust (Left FailError
    ferr) = MError -> Either MError a
    forall a b. a -> Either a b
    Left (MError -> Either MError a) -> MError -> Either MError a
    forall a b. (a -> b) -> a -> b
    $ FailError -> MError
    forall a. a -> Maybe a
    Just FailError
    ferr
    weaveJust (Right a
    a) = a -> Either MError a
    forall a b. b -> Either a b
    Right a
    a

    -- | Switches current pointman to the next on the level, if any, wrapping.
    pointmanCycleLevel :: MonadClientUI m
                       => ActorId -> Bool -> Direction -> m MError
    pointmanCycleLevel :: forall (m :: * -> *).
    MonadClientUI m =>
    ActorId -> Bool -> Direction -> m MError
    pointmanCycleLevel ActorId
    leader Bool
    verbose Direction
    direction = do
      side <- (StateClient -> FactionId) -> m FactionId
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> FactionId
    sside
      fact <- getsState $ (EM.! side) . sfactionD
      lidV <- viewedLevelUI
      body <- getsState $ getActorBody leader
      hs <- partyAfterLeader leader
      let banned = Faction -> Bool
    bannedPointmanSwitchBetweenLevels Faction
    fact
          hsSort = case Direction
    direction of
            Direction
    Forward -> [(ActorId, Actor, ActorUI)]
    hs
            Direction
    Backward -> [(ActorId, Actor, ActorUI)] -> [(ActorId, Actor, ActorUI)]
    forall a. [a] -> [a]
    reverse [(ActorId, Actor, ActorUI)]
    hs
      case filter (\(ActorId
    _, Actor
    b, ActorUI
    _) -> Actor -> LevelId
    blid Actor
    b LevelId -> LevelId -> Bool
    forall a. Eq a => a -> a -> Bool
    == LevelId
    lidV) hsSort of
        [(ActorId, Actor, ActorUI)]
    _ | Bool
    banned Bool -> Bool -> Bool
    && LevelId
    lidV LevelId -> LevelId -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Actor -> LevelId
    blid Actor
    body ->
          Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg (Text -> m MError) -> Text -> m MError
    forall a b. (a -> b) -> a -> b
    $ ReqFailure -> Text
    showReqFailure ReqFailure
    NoChangeDunLeader
        [] -> Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg Text
    "cannot pick any other pointman on this level"
        (ActorId
    np, Actor
    _b, ActorUI
    _) : [(ActorId, Actor, ActorUI)]
    _ -> do
          _success <- Bool -> ActorId -> m Bool
    forall (m :: * -> *). MonadClientUI m => Bool -> ActorId -> m Bool
    pickLeader Bool
    verbose ActorId
    np
    -- This apparently crashes rarely, probably becuase @leader@ gets out of sync
    -- with the leader in client state, during nested UI manipulations.
    -- The worst that can happen is TAB doing nothing and having to be repeated
    -- or perhaps item manipulation needs to be exited for TAB to work again.
    --      let !_A = assert (success `blame` "same leader"
    --                                `swith` (leader, np, b)) ()
          return Nothing

    -- | Switches current pointman to the previous in the whole dungeon, wrapping.
    pointmanCycle :: MonadClientUI m
                  => ActorId -> Bool -> Direction -> m MError
    pointmanCycle :: forall (m :: * -> *).
    MonadClientUI m =>
    ActorId -> Bool -> Direction -> m MError
    pointmanCycle ActorId
    leader Bool
    verbose Direction
    direction = do
      side <- (StateClient -> FactionId) -> m FactionId
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> FactionId
    sside
      fact <- getsState $ (EM.! side) . sfactionD
      hs <- partyAfterLeader leader
      let banned = Faction -> Bool
    bannedPointmanSwitchBetweenLevels Faction
    fact
          hsSort = case Direction
    direction of
            Direction
    Forward -> [(ActorId, Actor, ActorUI)]
    hs
            Direction
    Backward -> [(ActorId, Actor, ActorUI)] -> [(ActorId, Actor, ActorUI)]
    forall a. [a] -> [a]
    reverse [(ActorId, Actor, ActorUI)]
    hs
      case hsSort of
        [(ActorId, Actor, ActorUI)]
    _ | Bool
    banned -> Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg (Text -> m MError) -> Text -> m MError
    forall a b. (a -> b) -> a -> b
    $ ReqFailure -> Text
    showReqFailure ReqFailure
    NoChangeDunLeader
        [] -> Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg Text
    "no other member in the party"
        (ActorId
    np, Actor
    b, ActorUI
    _) : [(ActorId, Actor, ActorUI)]
    _ -> do
          success <- Bool -> ActorId -> m Bool
    forall (m :: * -> *). MonadClientUI m => Bool -> ActorId -> m Bool
    pickLeader Bool
    verbose ActorId
    np
          let !_A = Bool -> () -> ()
    forall a. HasCallStack => Bool -> a -> a
    assert (Bool
    success Bool -> (String, (ActorId, ActorId, Actor)) -> Bool
    forall v. Show v => Bool -> v -> Bool
    `blame` String
    "same leader"
                                    String
    -> (ActorId, ActorId, Actor) -> (String, (ActorId, ActorId, Actor))
    forall v. String -> v -> (String, v)
    `swith` (ActorId
    leader, ActorId
    np, Actor
    b)) ()
          return Nothing

    partyAfterLeader :: MonadClientUI m => ActorId -> m [(ActorId, Actor, ActorUI)]
    partyAfterLeader :: forall (m :: * -> *).
    MonadClientUI m =>
    ActorId -> m [(ActorId, Actor, ActorUI)]
    partyAfterLeader ActorId
    leader = do
      side <- (StateClient -> FactionId) -> m FactionId
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> FactionId
    sside
      sactorUI <- getsSession sactorUI
      allOurs <- getsState $ fidActorNotProjGlobalAssocs side -- not only on level
      let allOursUI = ((ActorId, Actor) -> (ActorId, Actor, ActorUI))
    -> [(ActorId, Actor)] -> [(ActorId, Actor, ActorUI)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(ActorId
    aid, Actor
    b) -> (ActorId
    aid, Actor
    b, ActorDictUI
    sactorUI ActorDictUI -> ActorId -> ActorUI
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! ActorId
    aid)) [(ActorId, Actor)]
    allOurs
          hs = ((ActorId, Actor, ActorUI)
     -> (Bool, Bool, Bool, Char, Color, ActorId))
    -> [(ActorId, Actor, ActorUI)] -> [(ActorId, Actor, ActorUI)]
    forall b a. Ord b => (a -> b) -> [a] -> [a]
    sortOn (ActorId, Actor, ActorUI)
    -> (Bool, Bool, Bool, Char, Color, ActorId)
    keySelected [(ActorId, Actor, ActorUI)]
    allOursUI
          i = Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe (-Int
    1) (Maybe Int -> Int) -> Maybe Int -> Int
    forall a b. (a -> b) -> a -> b
    $ ((ActorId, Actor, ActorUI) -> Bool)
    -> [(ActorId, Actor, ActorUI)] -> Maybe Int
    forall a. (a -> Bool) -> [a] -> Maybe Int
    findIndex (\(ActorId
    aid, Actor
    _, ActorUI
    _) -> ActorId
    aid ActorId -> ActorId -> Bool
    forall a. Eq a => a -> a -> Bool
    == ActorId
    leader) [(ActorId, Actor, ActorUI)]
    hs
          (lt, gt) = (take i hs, drop (i + 1) hs)
      return $! gt ++ lt

    -- | Select a faction leader. False, if nothing to do.
    pickLeader :: MonadClientUI m => Bool -> ActorId -> m Bool
    pickLeader :: forall (m :: * -> *). MonadClientUI m => Bool -> ActorId -> m Bool
    pickLeader Bool
    verbose ActorId
    aid = do
      mleader <- (StateClient -> Maybe ActorId) -> m (Maybe ActorId)
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> Maybe ActorId
    sleader
      if mleader == Just aid
        then return False -- already picked
        else do
          body <- getsState $ getActorBody aid
          bodyUI <- getsSession $ getActorUI aid
          let !_A = Bool -> () -> ()
    forall a. HasCallStack => Bool -> a -> a
    assert (Bool -> Bool
    not (Actor -> Bool
    bproj Actor
    body)
                            Bool -> (String, (ActorId, Actor)) -> Bool
    forall v. Show v => Bool -> v -> Bool
    `blame` String
    "projectile chosen as the pointman"
                            String -> (ActorId, Actor) -> (String, (ActorId, Actor))
    forall v. String -> v -> (String, v)
    `swith` (ActorId
    aid, Actor
    body)) ()
          -- Even if it's already the leader, give his proper name, not 'you'.
          let subject = ActorUI -> Part
    partActor ActorUI
    bodyUI
          when verbose $
            msgAdd MsgPointmanSwap $ makeSentence [subject, "picked as a pointman"]
          -- Update client state.
          updateClientLeader aid
          -- Move the xhair, if active, to the new level.
          modifySession $ \SessionUI
    sess -> SessionUI
    sess {saimMode =
            (\AimMode
    aimMode -> AimMode
    aimMode {aimLevelId = blid body}) <$> saimMode sess}
          -- Inform about items, etc.
          saimMode <- getsSession saimMode
          when verbose $
            if isJust saimMode
            then doLook
            else do
              (itemsBlurb, _) <-
                lookAtItems True (bpos body) (blid body) (Just aid) Nothing
              stashBlurb <- lookAtStash (bpos body) (blid body)
              msgAdd MsgAtFeetMinor $ stashBlurb <+> itemsBlurb
          return True

    -- | Perform look around in the current position of the xhair.
    -- Does nothing outside aiming mode.
    doLook :: MonadClientUI m => m ()
    doLook :: forall (m :: * -> *). MonadClientUI m => m ()
    doLook = do
      saimMode <- (SessionUI -> Maybe AimMode) -> m (Maybe AimMode)
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> Maybe AimMode
    saimMode
      case saimMode of
        Just AimMode
    aimMode -> do
          let lidV :: LevelId
    lidV = AimMode -> LevelId
    aimLevelId AimMode
    aimMode
          mxhairPos <- m (Maybe Point)
    forall (m :: * -> *). MonadClientUI m => m (Maybe Point)
    mxhairToPos
          xhairPos <- xhairToPos
          blurb <- lookAtPosition xhairPos lidV
          itemSel <- getsSession sitemSel
          mleader <- getsClient sleader
          outOfRangeBlurb <- case (itemSel, mxhairPos, mleader) of
            (Just (ItemId
    iid, CStore
    _, Bool
    _), Just Point
    pos, Just ActorId
    leader) -> do
              b <- (State -> Actor) -> m Actor
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState ((State -> Actor) -> m Actor) -> (State -> Actor) -> m Actor
    forall a b. (a -> b) -> a -> b
    $ ActorId -> State -> Actor
    getActorBody ActorId
    leader
              if lidV /= blid b  -- no range warnings on remote levels
                 || detailLevel aimMode < DetailHigh  -- no spam
              then return []
              else do
                itemFull <- getsState $ itemToFull iid
                let arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
                return [ (MsgPromptGeneric, "This position is out of range when flinging the selected item.")
                       | 1 + IA.totalRange arItem (itemKind itemFull)
                         < chessDist (bpos b) pos ]
            (Maybe (ItemId, CStore, Bool), Maybe Point, Maybe ActorId)
    _ -> [(MsgClassShow, Text)] -> m [(MsgClassShow, Text)]
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
          mapM_ (uncurry msgAdd) $ blurb ++ outOfRangeBlurb
        Maybe AimMode
    _ -> () -> m ()
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

    pickLeaderWithPointer :: MonadClientUI m => ActorId -> m MError
    pickLeaderWithPointer :: forall (m :: * -> *). MonadClientUI m => ActorId -> m MError
    pickLeaderWithPointer ActorId
    leader = do
      CCUI{coscreen=ScreenContent{rheight}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      lidV <- viewedLevelUI
      side <- getsClient sside
      fact <- getsState $ (EM.! side) . sfactionD
      arena <- getArenaUI
      sactorUI <- getsSession sactorUI
      ours <- getsState $ filter (not . bproj . snd)
                          . actorAssocs (== side) lidV
      let oursUI = ((ActorId, Actor) -> (ActorId, Actor, ActorUI))
    -> [(ActorId, Actor)] -> [(ActorId, Actor, ActorUI)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(ActorId
    aid, Actor
    b) -> (ActorId
    aid, Actor
    b, ActorDictUI
    sactorUI ActorDictUI -> ActorId -> ActorUI
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! ActorId
    aid)) [(ActorId, Actor)]
    ours
          viewed = ((ActorId, Actor, ActorUI)
     -> (Bool, Bool, Bool, Char, Color, ActorId))
    -> [(ActorId, Actor, ActorUI)] -> [(ActorId, Actor, ActorUI)]
    forall b a. Ord b => (a -> b) -> [a] -> [a]
    sortOn (ActorId, Actor, ActorUI)
    -> (Bool, Bool, Bool, Char, Color, ActorId)
    keySelected [(ActorId, Actor, ActorUI)]
    oursUI
          banned = Faction -> Bool
    bannedPointmanSwitchBetweenLevels Faction
    fact
          pick (ActorId
    aid, Actor
    b) = if Actor -> LevelId
    blid Actor
    b LevelId -> LevelId -> Bool
    forall a. Eq a => a -> a -> Bool
    /= LevelId
    arena Bool -> Bool -> Bool
    && Bool
    banned
                          then Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg (Text -> m MError) -> Text -> m MError
    forall a b. (a -> b) -> a -> b
    $ ReqFailure -> Text
    showReqFailure ReqFailure
    NoChangeDunLeader
                          else do
                            m Bool -> m ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (m Bool -> m ()) -> m Bool -> m ()
    forall a b. (a -> b) -> a -> b
    $ Bool -> ActorId -> m Bool
    forall (m :: * -> *). MonadClientUI m => Bool -> ActorId -> m Bool
    pickLeader Bool
    True ActorId
    aid
                            MError -> m MError
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return MError
    forall a. Maybe a
    Nothing
      pUI <- getsSession spointer
      let p@(Point px py) = squareToMap $ uiToSquare pUI
      -- Pick even if no space in status line for the actor's symbol.
      if | py == rheight - 2 && px == 0 -> pointmanCycle leader True Forward
         | py == rheight - 2 ->
             case drop (px - 1) viewed of
               [] -> MError -> m MError
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return MError
    forall a. Maybe a
    Nothing
                 -- relaxed, due to subtleties of display of selected actors
               (ActorId
    aid, Actor
    b, ActorUI
    _) : [(ActorId, Actor, ActorUI)]
    _ -> (ActorId, Actor) -> m MError
    pick (ActorId
    aid, Actor
    b)
         | otherwise ->
             case find (\(ActorId
    _, Actor
    b, ActorUI
    _) -> Actor -> Point
    bpos Actor
    b Point -> Point -> Bool
    forall a. Eq a => a -> a -> Bool
    == Point
    p) oursUI of
               Maybe (ActorId, Actor, ActorUI)
    Nothing -> Text -> m MError
    forall (m :: * -> *). MonadClientUI m => Text -> m MError
    failMsg Text
    "not pointing at an actor"
               Just (ActorId
    aid, Actor
    b, ActorUI
    _) -> (ActorId, Actor) -> m MError
    pick (ActorId
    aid, Actor
    b)

    itemOverlayFromState :: LevelId -> [(ItemId, ItemQuant)] -> Bool
                         -> CCUI -> FactionId -> DiscoveryBenefit -> FontSetup
                         -> State
                         -> OKX
    itemOverlayFromState :: LevelId
    -> [(ItemId, ItemQuant)]
    -> Bool
    -> CCUI
    -> FactionId
    -> DiscoveryBenefit
    -> FontSetup
    -> State
    -> OKX
    itemOverlayFromState LevelId
    arena [(ItemId, ItemQuant)]
    iids Bool
    displayRanged CCUI
    sccui FactionId
    side DiscoveryBenefit
    discoBenefit
                         FontSetup{DisplayFont
    squareFont :: DisplayFont
    monoFont :: DisplayFont
    propFont :: DisplayFont
    propFont :: FontSetup -> DisplayFont
    monoFont :: FontSetup -> DisplayFont
    squareFont :: FontSetup -> DisplayFont
    ..} State
    s =
      let CCUI{coscreen :: CCUI -> ScreenContent
    coscreen=ScreenContent{Int
    rwidth :: Int
    rwidth :: ScreenContent -> Int
    rwidth}} = CCUI
    sccui
          localTime :: Time
    localTime = LevelId -> State -> Time
    getLocalTime LevelId
    arena State
    s
          itemToF :: ItemId -> ItemFull
    itemToF = (ItemId -> State -> ItemFull) -> State -> ItemId -> ItemFull
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ItemId -> State -> ItemFull
    itemToFull State
    s
          factionD :: EnumMap FactionId Faction
    factionD = State -> EnumMap FactionId Faction
    sfactionD State
    s
          attrCursor :: Attr
    attrCursor = Attr
    Color.defAttr {Color.bg = Color.HighlightNoneCursor}
          markEqp :: Bool -> a -> a -> Char
    markEqp Bool
    periodic a
    k a
    ncha =
            if | Bool
    periodic -> Char
    '"'  -- if equipped, no charges
               | a
    ncha a -> a -> Bool
    forall a. Eq a => a -> a -> Bool
    == a
    0 -> Char
    '-'  -- no charges left
               | a
    k a -> a -> Bool
    forall a. Ord a => a -> a -> Bool
    > a
    ncha -> Char
    '~'  -- not all charges left
               | Bool
    otherwise -> Char
    '+'
          pr :: MenuSlot -> (ItemId, ItemQuant)
             -> (AttrString, AttrString, KeyOrSlot)
          pr :: MenuSlot
    -> (ItemId, ItemQuant) -> (AttrString, AttrString, KeyOrSlot)
    pr MenuSlot
    c (ItemId
    iid, kit :: ItemQuant
    kit@(Int
    k, ItemTimers
    _)) =
            let itemFull :: ItemFull
    itemFull = ItemId -> ItemFull
    itemToF ItemId
    iid
                arItem :: AspectRecord
    arItem = ItemFull -> AspectRecord
    aspectRecordFull ItemFull
    itemFull
                colorSymbol :: AttrCharW32
    colorSymbol =
                  if Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Condition AspectRecord
    arItem
                  then DiscoveryBenefit -> ItemId -> ItemFull -> AttrCharW32
    viewItemBenefitColored DiscoveryBenefit
    discoBenefit ItemId
    iid ItemFull
    itemFull
                  else ItemFull -> AttrCharW32
    viewItem ItemFull
    itemFull
                phrase :: Text
    phrase = [Part] -> Text
    makePhrase
                  [Int
    -> FactionId
    -> EnumMap FactionId Faction
    -> Bool
    -> DetailLevel
    -> Int
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsRanged Int
    rwidth FactionId
    side EnumMap FactionId Faction
    factionD Bool
    displayRanged
                                    DetailLevel
    DetailLow Int
    4 Int
    k Time
    localTime ItemFull
    itemFull ItemQuant
    kit]
                ncha :: Int
    ncha = Time -> ItemQuant -> Int
    ncharges Time
    localTime ItemQuant
    kit
                periodic :: Bool
    periodic = Flag -> AspectRecord -> Bool
    IA.checkFlag Flag
    Ability.Periodic AspectRecord
    arItem
                !cLab :: AttrChar
    cLab = Color.AttrChar { acAttr :: Attr
    acAttr = Attr
    attrCursor
                                       , acChar :: Char
    acChar = Bool -> Int -> Int -> Char
    forall {a}. (Num a, Ord a) => Bool -> a -> a -> Char
    markEqp Bool
    periodic Int
    k Int
    ncha }
                asLab :: AttrString
    asLab = [AttrChar -> AttrCharW32
    Color.attrCharToW32 AttrChar
    cLab]
                        AttrString -> AttrString -> AttrString
    forall a. [a] -> [a] -> [a]
    ++ [AttrCharW32
    Color.spaceAttrW32 | DisplayFont -> Bool
    isSquareFont DisplayFont
    propFont]
                        AttrString -> AttrString -> AttrString
    forall a. [a] -> [a] -> [a]
    ++ [AttrCharW32
    colorSymbol]
                !tDesc :: Text
    tDesc = Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    phrase
            in (AttrString
    asLab, Text -> AttrString
    textToAS Text
    tDesc, MenuSlot -> KeyOrSlot
    forall a b. b -> Either a b
    Right MenuSlot
    c)
          l :: [(AttrString, AttrString, KeyOrSlot)]
    l = (MenuSlot
     -> (ItemId, ItemQuant) -> (AttrString, AttrString, KeyOrSlot))
    -> [MenuSlot]
    -> [(ItemId, ItemQuant)]
    -> [(AttrString, AttrString, KeyOrSlot)]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith MenuSlot
    -> (ItemId, ItemQuant) -> (AttrString, AttrString, KeyOrSlot)
    pr [MenuSlot]
    natSlots [(ItemId, ItemQuant)]
    iids
      in DisplayFont
    -> DisplayFont -> [(AttrString, AttrString, KeyOrSlot)] -> OKX
    labDescOKX DisplayFont
    squareFont DisplayFont
    propFont [(AttrString, AttrString, KeyOrSlot)]
    l

    -- | Extract whole-dungeon statistics for each place kind,
    -- counting the number of occurrences of each type of
    -- `Game.LambdaHack.Content.PlaceKind.PlaceEntry`
    -- for the given place kind and gathering the set of levels
    -- on which any entry for that place kind can be found.
    placesFromState :: ContentData PK.PlaceKind -> Bool -> State
                    -> EM.EnumMap (ContentId PK.PlaceKind)
                                  (ES.EnumSet LevelId, Int, Int, Int)
    placesFromState :: ContentData PlaceKind
    -> Bool
    -> State
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    placesFromState ContentData PlaceKind
    coplace Bool
    sexposePlaces State
    s =
      let addEntries :: (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries (!EnumSet k
    es1, !b
    nEntries1, !c
    nArounds1, !d
    nExists1)
                     (!EnumSet k
    es2, !b
    nEntries2, !c
    nArounds2, !d
    nExists2) =
            let !es :: EnumSet k
    es = EnumSet k -> EnumSet k -> EnumSet k
    forall k. EnumSet k -> EnumSet k -> EnumSet k
    ES.union EnumSet k
    es1 EnumSet k
    es2
                !nEntries :: b
    nEntries = b
    nEntries1 b -> b -> b
    forall a. Num a => a -> a -> a
    + b
    nEntries2
                !nArounds :: c
    nArounds = c
    nArounds1 c -> c -> c
    forall a. Num a => a -> a -> a
    + c
    nArounds2
                !nExists :: d
    nExists = d
    nExists1 d -> d -> d
    forall a. Num a => a -> a -> a
    + d
    nExists2
            in (EnumSet k
    es, b
    nEntries, c
    nArounds, d
    nExists)
          placesFromLevel :: (LevelId, Level)
                          -> EM.EnumMap (ContentId PK.PlaceKind)
                                        (ES.EnumSet LevelId, Int, Int, Int)
          placesFromLevel :: (LevelId, Level)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    placesFromLevel (!LevelId
    lid, Level{EntryMap
    lentry :: EntryMap
    lentry :: Level -> EntryMap
    lentry}) =
            let f :: PlaceEntry
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    f (PK.PEntry ContentId PlaceKind
    pk) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em =
                  ((EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int))
    -> ContentId PlaceKind
    -> (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a.
    Enum k =>
    (a -> a -> a) -> k -> a -> EnumMap k a -> EnumMap k a
    EM.insertWith (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    forall {d} {c} {b} {k}.
    (Num d, Num c, Num b) =>
    (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries ContentId PlaceKind
    pk (LevelId -> EnumSet LevelId
    forall k. Enum k => k -> EnumSet k
    ES.singleton LevelId
    lid, Int
    1, Int
    0, Int
    0) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em
                f (PK.PAround ContentId PlaceKind
    pk) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em =
                  ((EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int))
    -> ContentId PlaceKind
    -> (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a.
    Enum k =>
    (a -> a -> a) -> k -> a -> EnumMap k a -> EnumMap k a
    EM.insertWith (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    forall {d} {c} {b} {k}.
    (Num d, Num c, Num b) =>
    (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries ContentId PlaceKind
    pk (LevelId -> EnumSet LevelId
    forall k. Enum k => k -> EnumSet k
    ES.singleton LevelId
    lid, Int
    0, Int
    1, Int
    0) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em
                f (PK.PExists ContentId PlaceKind
    pk) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em =
                  ((EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int))
    -> ContentId PlaceKind
    -> (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a.
    Enum k =>
    (a -> a -> a) -> k -> a -> EnumMap k a -> EnumMap k a
    EM.insertWith (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    forall {d} {c} {b} {k}.
    (Num d, Num c, Num b) =>
    (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries ContentId PlaceKind
    pk (LevelId -> EnumSet LevelId
    forall k. Enum k => k -> EnumSet k
    ES.singleton LevelId
    lid, Int
    0, Int
    0, Int
    1) EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    em
            in (PlaceEntry
     -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
     -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int))
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EntryMap
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall a b k. (a -> b -> b) -> b -> EnumMap k a -> b
    EM.foldr' PlaceEntry
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    f EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a. EnumMap k a
    EM.empty EntryMap
    lentry
                 -- go through place entrances and depending on the place
                 -- add an entry for it, whether Entry/Around/Exists,
                 -- the effect being we're counting #s of each type
          insertZeros :: EnumMap k (EnumSet k, b, c, d)
    -> k -> p -> EnumMap k (EnumSet k, b, c, d)
    insertZeros !EnumMap k (EnumSet k, b, c, d)
    em !k
    pk p
    _ = k
    -> (EnumSet k, b, c, d)
    -> EnumMap k (EnumSet k, b, c, d)
    -> EnumMap k (EnumSet k, b, c, d)
    forall k a. Enum k => k -> a -> EnumMap k a -> EnumMap k a
    EM.insert k
    pk (EnumSet k
    forall k. EnumSet k
    ES.empty, b
    0, c
    0, d
    0) EnumMap k (EnumSet k, b, c, d)
    em
          -- The initial places are overwritten except for those
          -- that have no entries in the dungeon at all,
          -- and in `sexposePlaces` debug mode these will be shown even though
          -- the stats will be zeros (which is a valuable warning!).
          initialPlaces :: EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    initialPlaces | Bool -> Bool
    not Bool
    sexposePlaces = EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a. EnumMap k a
    EM.empty
                        | Bool
    otherwise = ContentData PlaceKind
    -> (EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
        -> ContentId PlaceKind
        -> PlaceKind
        -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int))
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall a b. ContentData a -> (b -> ContentId a -> a -> b) -> b -> b
    ofoldlWithKey' ContentData PlaceKind
    coplace EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> ContentId PlaceKind
    -> PlaceKind
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall {k} {b} {c} {d} {k} {p}.
    (Enum k, Num b, Num c, Num d) =>
    EnumMap k (EnumSet k, b, c, d)
    -> k -> p -> EnumMap k (EnumSet k, b, c, d)
    insertZeros EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall k a. EnumMap k a
    EM.empty
      in ((EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int))
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall a k.
    (a -> a -> a) -> EnumMap k a -> EnumMap k a -> EnumMap k a
    EM.unionWith (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    forall {d} {c} {b} {k}.
    (Num d, Num c, Num b) =>
    (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries
           EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    initialPlaces
           (((EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int)
     -> (EnumSet LevelId, Int, Int, Int))
    -> [EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)]
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall a k. (a -> a -> a) -> [EnumMap k a] -> EnumMap k a
    EM.unionsWith (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    -> (EnumSet LevelId, Int, Int, Int)
    forall {d} {c} {b} {k}.
    (Num d, Num c, Num b) =>
    (EnumSet k, b, c, d)
    -> (EnumSet k, b, c, d) -> (EnumSet k, b, c, d)
    addEntries ([EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)]
     -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int))
    -> [EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)]
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    forall a b. (a -> b) -> a -> b
    $ ((LevelId, Level)
     -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int))
    -> [(LevelId, Level)]
    -> [EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)]
    forall a b. (a -> b) -> [a] -> [b]
    map (LevelId, Level)
    -> EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    placesFromLevel ([(LevelId, Level)]
     -> [EnumMap
           (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)])
    -> [(LevelId, Level)]
    -> [EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)]
    forall a b. (a -> b) -> a -> b
    $ EnumMap LevelId Level -> [(LevelId, Level)]
    forall k a. Enum k => EnumMap k a -> [(k, a)]
    EM.assocs (EnumMap LevelId Level -> [(LevelId, Level)])
    -> EnumMap LevelId Level -> [(LevelId, Level)]
    forall a b. (a -> b) -> a -> b
    $ State -> EnumMap LevelId Level
    sdungeon State
    s)
            -- gather per-place-kind statistics for each level,
            -- then aggregate them over all levels, remembering that the place
            -- appeared on the given level (but not how man times)

    -- TODO: if faction not known, it's info should not be updated
    -- by the server. But let's wait until server sends general state diffs
    -- and then block diffs that don't apply, because faction is missing.
    factionsFromState :: ItemRoles -> State -> [(FactionId, Faction)]
    factionsFromState :: ItemRoles -> State -> [(FactionId, Faction)]
    factionsFromState (ItemRoles EnumMap SLore (EnumSet ItemId)
    itemRoles) State
    s =
      let seenTrunks :: [ItemId]
    seenTrunks = EnumSet ItemId -> [ItemId]
    forall k. Enum k => EnumSet k -> [k]
    ES.toList (EnumSet ItemId -> [ItemId]) -> EnumSet ItemId -> [ItemId]
    forall a b. (a -> b) -> a -> b
    $ EnumMap SLore (EnumSet ItemId)
    itemRoles EnumMap SLore (EnumSet ItemId) -> SLore -> EnumSet ItemId
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! SLore
    STrunk
          trunkBelongs :: FactionId -> ItemId -> Bool
    trunkBelongs FactionId
    fid ItemId
    iid = Item -> Maybe FactionId
    jfid (ItemId -> State -> Item
    getItemBody ItemId
    iid State
    s) Maybe FactionId -> Maybe FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId -> Maybe FactionId
    forall a. a -> Maybe a
    Just FactionId
    fid
          factionSeen :: (FactionId, Faction) -> Bool
    factionSeen (FactionId
    fid, Faction
    fact) = Bool -> Bool
    not (EnumMap (ContentId ItemKind) Int -> Bool
    forall k a. EnumMap k a -> Bool
    EM.null (Faction -> EnumMap (ContentId ItemKind) Int
    gvictims Faction
    fact))  -- shortcut
                                    Bool -> Bool -> Bool
    || (ItemId -> Bool) -> [ItemId] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (FactionId -> ItemId -> Bool
    trunkBelongs FactionId
    fid) [ItemId]
    seenTrunks
      in ((FactionId, Faction) -> Bool)
    -> [(FactionId, Faction)] -> [(FactionId, Faction)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (FactionId, Faction) -> Bool
    factionSeen ([(FactionId, Faction)] -> [(FactionId, Faction)])
    -> [(FactionId, Faction)] -> [(FactionId, Faction)]
    forall a b. (a -> b) -> a -> b
    $ EnumMap FactionId Faction -> [(FactionId, Faction)]
    forall k a. Enum k => EnumMap k a -> [(k, a)]
    EM.assocs (EnumMap FactionId Faction -> [(FactionId, Faction)])
    -> EnumMap FactionId Faction -> [(FactionId, Faction)]
    forall a b. (a -> b) -> a -> b
    $ State -> EnumMap FactionId Faction
    sfactionD State
    s

    itemOverlay :: MonadClientUI m
                => [(ItemId, ItemQuant)] -> ItemDialogMode -> m OKX
    itemOverlay :: forall (m :: * -> *).
    MonadClientUI m =>
    [(ItemId, ItemQuant)] -> ItemDialogMode -> m OKX
    itemOverlay [(ItemId, ItemQuant)]
    iids ItemDialogMode
    dmode = do
      sccui <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      side <- getsClient sside
      arena <- getArenaUI
      discoBenefit <- getsClient sdiscoBenefit
      fontSetup <- getFontSetup
      let displayRanged =
            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, SLore -> ItemDialogMode
    MLore SLore
    SBlast ]
      okx <- getsState $ itemOverlayFromState arena iids displayRanged
                                              sccui side discoBenefit fontSetup
      return $! okx

    skillsOverlay :: MonadClientUI m => ActorId -> m OKX
    skillsOverlay :: forall (m :: * -> *). MonadClientUI m => ActorId -> m OKX
    skillsOverlay ActorId
    aid = do
      b <- (State -> Actor) -> m Actor
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState ((State -> Actor) -> m Actor) -> (State -> Actor) -> m Actor
    forall a b. (a -> b) -> a -> b
    $ ActorId -> State -> Actor
    getActorBody ActorId
    aid
      actorMaxSk <- getsState $ getActorMaxSkills aid
      FontSetup{..} <- getFontSetup
      let prSlot :: MenuSlot -> Ability.Skill
                 -> ((AttrLine, (Int, AttrLine), (Int, AttrLine)), KYX)
          prSlot MenuSlot
    c Skill
    skill =
            let skName :: Text
    skName = Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Skill -> Text
    skillName Skill
    skill
                attrCursor :: Attr
    attrCursor = Attr
    Color.defAttr {Color.bg = Color.HighlightNoneCursor}
                labAc :: AttrChar
    labAc = Color.AttrChar { acAttr :: Attr
    acAttr = Attr
    attrCursor
                                       , acChar :: Char
    acChar = Char
    '+' }
                lab :: AttrLine
    lab = AttrString -> AttrLine
    attrStringToAL [AttrChar -> AttrCharW32
    Color.attrCharToW32 AttrChar
    labAc]
                labLen :: Int
    labLen = DisplayFont -> AttrString -> Int
    forall a. DisplayFont -> [a] -> Int
    textSize DisplayFont
    squareFont (AttrString -> Int) -> AttrString -> Int
    forall a b. (a -> b) -> a -> b
    $ AttrLine -> AttrString
    attrLine AttrLine
    lab
                indentation :: Int
    indentation = if DisplayFont -> Bool
    isSquareFont DisplayFont
    propFont then Int
    52 else Int
    26
                valueText :: Text
    valueText = Skill -> Actor -> Int -> Text
    skillToDecorator Skill
    skill Actor
    b
                            (Int -> Text) -> Int -> Text
    forall a b. (a -> b) -> a -> b
    $ Skill -> Skills -> Int
    Ability.getSk Skill
    skill Skills
    actorMaxSk
                triple :: (AttrLine, (Int, AttrLine), (Int, AttrLine))
    triple = ( AttrLine
    lab
                         , (Int
    labLen, Text -> AttrLine
    textToAL Text
    skName)
                         , (Int
    indentation, Text -> AttrLine
    textToAL Text
    valueText) )
                lenButton :: Int
    lenButton = Int
    26 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Text -> Int
    T.length Text
    valueText
            in ((AttrLine, (Int, AttrLine), (Int, AttrLine))
    triple, (MenuSlot -> KeyOrSlot
    forall a b. b -> Either a b
    Right MenuSlot
    c, ( Int -> Int -> PointUI
    PointUI Int
    0 (MenuSlot -> Int
    forall a. Enum a => a -> Int
    fromEnum MenuSlot
    c)
                                  , DisplayFont -> Int -> ButtonWidth
    ButtonWidth DisplayFont
    propFont Int
    lenButton )))
          (ts, kxs) = unzip $ zipWith prSlot natSlots skillsInDisplayOrder
          (skLab, skDescr, skValue) = unzip3 ts
          skillLab = DisplayFont -> Overlay -> EnumMap DisplayFont Overlay
    forall k a. Enum k => k -> a -> EnumMap k a
    EM.singleton DisplayFont
    squareFont (Overlay -> EnumMap DisplayFont Overlay)
    -> Overlay -> EnumMap DisplayFont Overlay
    forall a b. (a -> b) -> a -> b
    $ [AttrLine] -> Overlay
    offsetOverlay [AttrLine]
    skLab
          skillDescr = DisplayFont -> Overlay -> EnumMap DisplayFont Overlay
    forall k a. Enum k => k -> a -> EnumMap k a
    EM.singleton DisplayFont
    propFont (Overlay -> EnumMap DisplayFont Overlay)
    -> Overlay -> EnumMap DisplayFont Overlay
    forall a b. (a -> b) -> a -> b
    $ [(Int, AttrLine)] -> Overlay
    offsetOverlayX [(Int, AttrLine)]
    skDescr
          skillValue = DisplayFont -> Overlay -> EnumMap DisplayFont Overlay
    forall k a. Enum k => k -> a -> EnumMap k a
    EM.singleton DisplayFont
    monoFont (Overlay -> EnumMap DisplayFont Overlay)
    -> Overlay -> EnumMap DisplayFont Overlay
    forall a b. (a -> b) -> a -> b
    $ [(Int, AttrLine)] -> Overlay
    offsetOverlayX [(Int, AttrLine)]
    skValue
      return (EM.unionsWith (++) [skillLab, skillDescr, skillValue], kxs)

    placesOverlay :: MonadClientUI m => m OKX
    placesOverlay :: forall (m :: * -> *). MonadClientUI m => m OKX
    placesOverlay = do
      COps{coplace} <- (State -> COps) -> m COps
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState State -> COps
    scops
      soptions <- getsClient soptions
      FontSetup{..} <- getFontSetup
      places <- getsState $ placesFromState coplace (sexposePlaces soptions)
      let prSlot :: MenuSlot
                 -> (ContentId PK.PlaceKind, (ES.EnumSet LevelId, Int, Int, Int))
                 -> (AttrString, AttrString, KeyOrSlot)
          prSlot MenuSlot
    c (ContentId PlaceKind
    pk, (EnumSet LevelId
    es, Int
    _, Int
    _, Int
    _)) =
            let name :: Text
    name = PlaceKind -> Text
    PK.pname (PlaceKind -> Text) -> PlaceKind -> Text
    forall a b. (a -> b) -> a -> b
    $ ContentData PlaceKind -> ContentId PlaceKind -> PlaceKind
    forall a. ContentData a -> ContentId a -> a
    okind ContentData PlaceKind
    coplace ContentId PlaceKind
    pk
                labChar :: Char
    labChar = if EnumSet LevelId -> Bool
    forall k. EnumSet k -> Bool
    ES.null EnumSet LevelId
    es then Char
    '-' else Char
    '+'
                attrCursor :: Attr
    attrCursor = Attr
    Color.defAttr {Color.bg = Color.HighlightNoneCursor}
                labAc :: AttrChar
    labAc = Color.AttrChar { acAttr :: Attr
    acAttr = Attr
    attrCursor
                                       , acChar :: Char
    acChar = Char
    labChar }
                -- Bang required to free @places@ as you go.
                !asLab :: AttrString
    asLab = [AttrChar -> AttrCharW32
    Color.attrCharToW32 AttrChar
    labAc]
                !tDesc :: Text
    tDesc = Text
    " "
                         Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    name
                         Text -> Text -> Text
    <+> if EnumSet LevelId -> Bool
    forall k. EnumSet k -> Bool
    ES.null EnumSet LevelId
    es
                             then Text
    ""
                             else Text
    "("
                                  Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> [Part] -> Text
    makePhrase [Int -> Part -> Part
    MU.CarWs (EnumSet LevelId -> Int
    forall k. EnumSet k -> Int
    ES.size EnumSet LevelId
    es) Part
    "level"]
                                  Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
            in (AttrString
    asLab, Text -> AttrString
    textToAS Text
    tDesc, MenuSlot -> KeyOrSlot
    forall a b. b -> Either a b
    Right MenuSlot
    c)
          l = (MenuSlot
     -> (ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))
     -> (AttrString, AttrString, KeyOrSlot))
    -> [MenuSlot]
    -> [(ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))]
    -> [(AttrString, AttrString, KeyOrSlot)]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith MenuSlot
    -> (ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))
    -> (AttrString, AttrString, KeyOrSlot)
    prSlot [MenuSlot]
    natSlots ([(ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))]
     -> [(AttrString, AttrString, KeyOrSlot)])
    -> [(ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))]
    -> [(AttrString, AttrString, KeyOrSlot)]
    forall a b. (a -> b) -> a -> b
    $ EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    -> [(ContentId PlaceKind, (EnumSet LevelId, Int, Int, Int))]
    forall k a. Enum k => EnumMap k a -> [(k, a)]
    EM.assocs EnumMap (ContentId PlaceKind) (EnumSet LevelId, Int, Int, Int)
    places
      return $! labDescOKX squareFont propFont l

    factionsOverlay :: MonadClientUI m => m OKX
    factionsOverlay :: forall (m :: * -> *). MonadClientUI m => m OKX
    factionsOverlay = do
      FontSetup{..} <- m FontSetup
    forall (m :: * -> *). MonadClientUI m => m FontSetup
    getFontSetup
      sroles <- getsSession sroles
      factions <- getsState $ factionsFromState sroles
      let prSlot :: MenuSlot
                 -> (FactionId, Faction)
                 -> (AttrString, AttrString, KeyOrSlot)
          prSlot MenuSlot
    c (FactionId
    _, Faction
    fact) =
            let name :: Text
    name = FactionKind -> Text
    FK.fname (FactionKind -> Text) -> FactionKind -> Text
    forall a b. (a -> b) -> a -> b
    $ Faction -> FactionKind
    gkind Faction
    fact  -- we ignore "Controlled", etc.
                gameOver :: Bool
    gameOver = Maybe Status -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe Status -> Bool) -> Maybe Status -> Bool
    forall a b. (a -> b) -> a -> b
    $ Faction -> Maybe Status
    gquit Faction
    fact
                labChar :: Char
    labChar = if Bool
    gameOver then Char
    '-' else Char
    '+'
                attrCursor :: Attr
    attrCursor = Attr
    Color.defAttr {Color.bg = Color.HighlightNoneCursor}
                labAc :: AttrChar
    labAc = Color.AttrChar { acAttr :: Attr
    acAttr = Attr
    attrCursor
                                       , acChar :: Char
    acChar = Char
    labChar }
                !asLab :: AttrString
    asLab = [AttrChar -> AttrCharW32
    Color.attrCharToW32 AttrChar
    labAc]
                !tDesc :: Text
    tDesc = Text
    " "
                         Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    name
                         Text -> Text -> Text
    <+> case Faction -> Maybe Status
    gquit Faction
    fact of
                               Just Status{Outcome
    stOutcome :: Outcome
    stOutcome :: Status -> Outcome
    stOutcome} | Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ Faction -> Bool
    isHorrorFact Faction
    fact ->
                                 Text
    "(" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Outcome -> Text
    FK.nameOutcomePast Outcome
    stOutcome Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")"
                               Maybe Status
    _ -> Text
    ""
            in (AttrString
    asLab, Text -> AttrString
    textToAS Text
    tDesc, MenuSlot -> KeyOrSlot
    forall a b. b -> Either a b
    Right MenuSlot
    c)
          l = (MenuSlot
     -> (FactionId, Faction) -> (AttrString, AttrString, KeyOrSlot))
    -> [MenuSlot]
    -> [(FactionId, Faction)]
    -> [(AttrString, AttrString, KeyOrSlot)]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith MenuSlot
    -> (FactionId, Faction) -> (AttrString, AttrString, KeyOrSlot)
    prSlot [MenuSlot]
    natSlots [(FactionId, Faction)]
    factions
      return $! labDescOKX squareFont propFont l

    modesOverlay :: MonadClientUI m => m OKX
    modesOverlay :: forall (m :: * -> *). MonadClientUI m => m OKX
    modesOverlay = do
      COps{comode} <- (State -> COps) -> m COps
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState State -> COps
    scops
      FontSetup{..} <- getFontSetup
      svictories <- getsSession svictories
      nxtChal <- getsClient snxtChal  -- mark victories only for current difficulty
      let f ![(a, b)]
    acc p
    _p !a
    i !b
    a = (a
    i, b
    a) (a, b) -> [(a, b)] -> [(a, b)]
    forall a. a -> [a] -> [a]
    : [(a, b)]
    acc
          campaignModes = ContentData ModeKind
    -> GroupName ModeKind
    -> ([(ContentId ModeKind, ModeKind)]
        -> Int
        -> ContentId ModeKind
        -> ModeKind
        -> [(ContentId ModeKind, ModeKind)])
    -> [(ContentId ModeKind, ModeKind)]
    -> [(ContentId ModeKind, ModeKind)]
    forall a b.
    ContentData a
    -> GroupName a -> (b -> Int -> ContentId a -> a -> b) -> b -> b
    ofoldlGroup' ContentData ModeKind
    comode GroupName ModeKind
    MK.CAMPAIGN_SCENARIO [(ContentId ModeKind, ModeKind)]
    -> Int
    -> ContentId ModeKind
    -> ModeKind
    -> [(ContentId ModeKind, ModeKind)]
    forall {a} {b} {p}. [(a, b)] -> p -> a -> b -> [(a, b)]
    f []
          prSlot :: MenuSlot
                 -> (ContentId MK.ModeKind, MK.ModeKind)
                 -> (AttrString, AttrString, KeyOrSlot)
          prSlot MenuSlot
    c (ContentId ModeKind
    gameModeId, ModeKind
    gameMode) =
            let modeName :: Text
    modeName = ModeKind -> Text
    MK.mname ModeKind
    gameMode
                victories :: Int
    victories = case ContentId ModeKind
    -> EnumMap (ContentId ModeKind) (Map Challenge Int)
    -> Maybe (Map Challenge Int)
    forall k a. Enum k => k -> EnumMap k a -> Maybe a
    EM.lookup ContentId ModeKind
    gameModeId EnumMap (ContentId ModeKind) (Map Challenge Int)
    svictories of
                  Maybe (Map Challenge Int)
    Nothing -> Int
    0
                  Just Map Challenge Int
    cm -> Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    0 (Challenge -> Map Challenge Int -> Maybe Int
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup Challenge
    nxtChal Map Challenge Int
    cm)
                labChar :: Char
    labChar = if Int
    victories Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0 then Char
    '-' else Char
    '+'
                attrCursor :: Attr
    attrCursor = Attr
    Color.defAttr {Color.bg = Color.HighlightNoneCursor}
                labAc :: AttrChar
    labAc = Color.AttrChar { acAttr :: Attr
    acAttr = Attr
    attrCursor
                                       , acChar :: Char
    acChar = Char
    labChar }
                !asLab :: AttrString
    asLab = [AttrChar -> AttrCharW32
    Color.attrCharToW32 AttrChar
    labAc]
                !tDesc :: Text
    tDesc = Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    modeName
            in (AttrString
    asLab, Text -> AttrString
    textToAS Text
    tDesc, MenuSlot -> KeyOrSlot
    forall a b. b -> Either a b
    Right MenuSlot
    c)
          l = (MenuSlot
     -> (ContentId ModeKind, ModeKind)
     -> (AttrString, AttrString, KeyOrSlot))
    -> [MenuSlot]
    -> [(ContentId ModeKind, ModeKind)]
    -> [(AttrString, AttrString, KeyOrSlot)]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith MenuSlot
    -> (ContentId ModeKind, ModeKind)
    -> (AttrString, AttrString, KeyOrSlot)
    prSlot [MenuSlot]
    natSlots [(ContentId ModeKind, ModeKind)]
    campaignModes
      return $! labDescOKX squareFont propFont l

    describeMode :: MonadClientUI m
                 => Bool -> ContentId MK.ModeKind
                 -> m (EM.EnumMap DisplayFont Overlay)
    describeMode :: forall (m :: * -> *).
    MonadClientUI m =>
    Bool -> ContentId ModeKind -> m (EnumMap DisplayFont Overlay)
    describeMode Bool
    addTitle ContentId ModeKind
    gameModeId = do
      COps{comode} <- (State -> COps) -> m COps
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState State -> COps
    scops
      CCUI{coscreen=ScreenContent{rwidth}}
        <- getsSession sccui
      FontSetup{..} <- getFontSetup
      scoreDict <- getsState shigh
      scampings <- getsSession scampings
      srestarts <- getsSession srestarts
      side <- getsClient sside
      total <- getsState $ snd . calculateTotal side
      dungeonTotal <- getsState sgold
      let screensaverBlurb = Text
    "This is one of the screensaver scenarios, not available from the main menu, with all factions controlled by AI. Feel free to take over or relinquish control at any moment, but to register a legitimate high score, choose a standard scenario instead.\n"
      let gameMode = ContentData ModeKind -> ContentId ModeKind -> ModeKind
    forall a. ContentData a -> ContentId a -> a
    okind ContentData ModeKind
    comode ContentId ModeKind
    gameModeId
          duplicateEOL Char
    '\n' = Text
    "\n\n"
          duplicateEOL Char
    c = Char -> Text
    T.singleton Char
    c
          sections =
            [ ( Color -> Text -> AttrString
    textFgToAS Color
    Color.BrGreen Text
    "The story so far:"
              , (Char -> Text) -> Text -> Text
    T.concatMap Char -> Text
    duplicateEOL (ModeKind -> Text
    MK.mdesc ModeKind
    gameMode) )
            , ( Color -> Text -> AttrString
    textFgToAS Color
    Color.cMeta Text
    "Rules of the game:"
              , ModeKind -> Text
    MK.mrules ModeKind
    gameMode )
            , ( Color -> Text -> AttrString
    textFgToAS Color
    Color.BrCyan Text
    "Running commentary:"
              , (Char -> Text) -> Text -> Text
    T.concatMap Char -> Text
    duplicateEOL
                  (if ModeKind -> Bool
    MK.mattract ModeKind
    gameMode
                   then Text
    screensaverBlurb Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> ModeKind -> Text
    MK.mreason ModeKind
    gameMode
                   else ModeKind -> Text
    MK.mreason ModeKind
    gameMode) )
            , ( Color -> Text -> AttrString
    textFgToAS Color
    Color.cGreed Text
    "Hints, not needed unless stuck:"
              , (Char -> Text) -> Text -> Text
    T.concatMap Char -> Text
    duplicateEOL (ModeKind -> Text
    MK.mhint ModeKind
    gameMode) )
            ]
          renderSection :: (AttrString, Text) -> Maybe [(DisplayFont, AttrString)]
          renderSection (AttrString
    header, Text
    desc) =
            if Text -> Bool
    T.null Text
    desc
            then Maybe [(DisplayFont, AttrString)]
    forall a. Maybe a
    Nothing
            else [(DisplayFont, AttrString)] -> Maybe [(DisplayFont, AttrString)]
    forall a. a -> Maybe a
    Just [(DisplayFont
    monoFont, AttrString
    header), (DisplayFont
    propFont, Text -> AttrString
    textToAS Text
    desc)]
          survivingHow = if | Int
    total Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 -> Text
    "(barely)"
                            | Int
    total Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    dungeonTotal Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `div` Int
    2 -> Text
    "(so far)"
                            | Bool
    otherwise -> Text
    ""
          title = if Bool
    addTitle
                  then Text
    "\nYou are"
                       Text -> Text -> Text
    <+> Text
    survivingHow
                       Text -> Text -> Text
    <+> Text
    "surviving the '"
                       Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> ModeKind -> Text
    MK.mname ModeKind
    gameMode
                       Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "' adventure.\n"
                  else Text
    ""
          blurb = ((DisplayFont, AttrString) -> (DisplayFont, [AttrLine]))
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])]
    forall a b. (a -> b) -> [a] -> [b]
    map ((AttrString -> [AttrLine])
    -> (DisplayFont, AttrString) -> (DisplayFont, [AttrLine])
    forall b c d. (b -> c) -> (d, b) -> (d, c)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (d, b) (d, c)
    second ((AttrString -> [AttrLine])
     -> (DisplayFont, AttrString) -> (DisplayFont, [AttrLine]))
    -> (AttrString -> [AttrLine])
    -> (DisplayFont, AttrString)
    -> (DisplayFont, [AttrLine])
    forall a b. (a -> b) -> a -> b
    $ Int -> Int -> AttrString -> [AttrLine]
    splitAttrString (Int
    rwidth Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2) (Int
    rwidth Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2)) ([(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])])
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])]
    forall a b. (a -> b) -> a -> b
    $
            (DisplayFont
    propFont, Text -> AttrString
    textToAS (Text
    title Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "\n"))
            (DisplayFont, AttrString)
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, AttrString)]
    forall a. a -> [a] -> [a]
    : [(DisplayFont, AttrString)]
    -> [[(DisplayFont, AttrString)]] -> [(DisplayFont, AttrString)]
    forall a. [a] -> [[a]] -> [a]
    intercalate [(DisplayFont
    monoFont, Text -> AttrString
    textToAS Text
    "\n")]
                           (((AttrString, Text) -> Maybe [(DisplayFont, AttrString)])
    -> [(AttrString, Text)] -> [[(DisplayFont, AttrString)]]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe (AttrString, Text) -> Maybe [(DisplayFont, AttrString)]
    renderSection [(AttrString, Text)]
    sections)
          -- Colour is used to delimit the section when displayed in one
          -- column, when using square fonts only.
          blurbEnd = ((DisplayFont, AttrString) -> (DisplayFont, [AttrLine]))
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])]
    forall a b. (a -> b) -> [a] -> [b]
    map ((AttrString -> [AttrLine])
    -> (DisplayFont, AttrString) -> (DisplayFont, [AttrLine])
    forall b c d. (b -> c) -> (d, b) -> (d, c)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (d, b) (d, c)
    second ((AttrString -> [AttrLine])
     -> (DisplayFont, AttrString) -> (DisplayFont, [AttrLine]))
    -> (AttrString -> [AttrLine])
    -> (DisplayFont, AttrString)
    -> (DisplayFont, [AttrLine])
    forall a b. (a -> b) -> a -> b
    $ Int -> Int -> AttrString -> [AttrLine]
    splitAttrString (Int
    rwidth Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2) (Int
    rwidth Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2)) ([(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])])
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, [AttrLine])]
    forall a b. (a -> b) -> a -> b
    $
            ( DisplayFont
    propFont
            , Color -> Text -> AttrString
    textFgToAS Color
    Color.Brown
                         Text
    "\nThis adventure's endings experienced so far:\n\n" )
              (DisplayFont, AttrString)
    -> [(DisplayFont, AttrString)] -> [(DisplayFont, AttrString)]
    forall a. a -> [a] -> [a]
    : if [(DisplayFont, AttrString)] -> Bool
    forall a. [a] -> Bool
    null [(DisplayFont, AttrString)]
    sectionsEndAS
                then [(DisplayFont
    monoFont, Text -> AttrString
    textToAS Text
    "*none*")]
                else [(DisplayFont, AttrString)]
    sectionsEndAS
          sectionsEndAS = [(DisplayFont, AttrString)]
    -> [[(DisplayFont, AttrString)]] -> [(DisplayFont, AttrString)]
    forall a. [a] -> [[a]] -> [a]
    intercalate [(DisplayFont
    monoFont, Text -> AttrString
    textToAS Text
    "\n")]
                                      (((AttrString, Text) -> Maybe [(DisplayFont, AttrString)])
    -> [(AttrString, Text)] -> [[(DisplayFont, AttrString)]]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe (AttrString, Text) -> Maybe [(DisplayFont, AttrString)]
    renderSection [(AttrString, Text)]
    sectionsEnd)
          sectionsEnd = (Outcome -> (AttrString, Text))
    -> [Outcome] -> [(AttrString, Text)]
    forall a b. (a -> b) -> [a] -> [b]
    map Outcome -> (AttrString, Text)
    outcomeSection [Outcome
    forall a. Bounded a => a
    minBound..Outcome
    forall a. Bounded a => a
    maxBound]
          outcomeSection :: FK.Outcome -> (AttrString, Text)
          outcomeSection Outcome
    outcome =
            ( Outcome -> AttrString
    renderOutcome Outcome
    outcome
            , if Bool -> Bool
    not (Outcome -> Bool
    outcomeSeen Outcome
    outcome)
              then Text
    ""  -- a possible spoiler and lack of sense of progression
              else (Char -> Text) -> Text -> Text
    T.concatMap Char -> Text
    duplicateEOL
                   (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "" (Maybe Text -> Text) -> Maybe Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Outcome -> [(Outcome, Text)] -> Maybe Text
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup Outcome
    outcome
                   ([(Outcome, Text)] -> Maybe Text)
    -> [(Outcome, Text)] -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ ModeKind -> [(Outcome, Text)]
    MK.mendMsg ModeKind
    gameMode [(Outcome, Text)] -> [(Outcome, Text)] -> [(Outcome, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(Outcome, Text)]
    endMsgDefault  -- left-biased
            )
          -- These are not added to @mendMsg@, because they only fit here.
          endMsgDefault =
            [ (Outcome
    FK.Restart, Text
    "No shame there is in noble defeat and there is honour in perseverance. Sometimes there are ways and places to turn rout into victory.")
            , (Outcome
    FK.Camping, Text
    "Don't fear to take breaks. While you move, others move, even on distant floors, but while you stay still, the world stays still.")
            ]
          scoreRecords = [ScoreRecord]
    -> (ScoreTable -> [ScoreRecord])
    -> Maybe ScoreTable
    -> [ScoreRecord]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [] ScoreTable -> [ScoreRecord]
    HighScore.unTable (Maybe ScoreTable -> [ScoreRecord])
    -> Maybe ScoreTable -> [ScoreRecord]
    forall a b. (a -> b) -> a -> b
    $ ContentId ModeKind -> ScoreDict -> Maybe ScoreTable
    forall k a. Enum k => k -> EnumMap k a -> Maybe a
    EM.lookup ContentId ModeKind
    gameModeId ScoreDict
    scoreDict
          -- This doesn't use @svictories@, but high scores, because high scores
          -- are more persistent and granular (per-outcome). OTOH, @svictories@
          -- are per-challenge, which is important in other cases.
          -- @Camping@ and @Restart@ are fine to be less persistent.
          outcomeSeen :: FK.Outcome -> Bool
          outcomeSeen Outcome
    outcome = case Outcome
    outcome of
            Outcome
    FK.Camping -> ContentId ModeKind
    gameModeId ContentId ModeKind -> EnumSet (ContentId ModeKind) -> Bool
    forall k. Enum k => k -> EnumSet k -> Bool
    `ES.member` EnumSet (ContentId ModeKind)
    scampings
            Outcome
    FK.Restart -> ContentId ModeKind
    gameModeId ContentId ModeKind -> EnumSet (ContentId ModeKind) -> Bool
    forall k. Enum k => k -> EnumSet k -> Bool
    `ES.member` EnumSet (ContentId ModeKind)
    srestarts
            Outcome
    _ -> Outcome
    outcome Outcome -> [Outcome] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (ScoreRecord -> Outcome) -> [ScoreRecord] -> [Outcome]
    forall a b. (a -> b) -> [a] -> [b]
    map (Status -> Outcome
    stOutcome (Status -> Outcome)
    -> (ScoreRecord -> Status) -> ScoreRecord -> Outcome
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ScoreRecord -> Status
    HighScore.getStatus) [ScoreRecord]
    scoreRecords
          -- Camping not taken into account.
          lastOutcome :: FK.Outcome
          lastOutcome = if [ScoreRecord] -> Bool
    forall a. [a] -> Bool
    null [ScoreRecord]
    scoreRecords
                        then Outcome
    FK.Restart  -- only if nothing else
                        else Status -> Outcome
    stOutcome (Status -> Outcome)
    -> (ScoreRecord -> Status) -> ScoreRecord -> Outcome
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ScoreRecord -> Status
    HighScore.getStatus
                             (ScoreRecord -> Outcome) -> ScoreRecord -> Outcome
    forall a b. (a -> b) -> a -> b
    $ (ScoreRecord -> ScoreRecord -> Ordering)
    -> [ScoreRecord] -> ScoreRecord
    forall (t :: * -> *) a.
    Foldable t =>
    (a -> a -> Ordering) -> t a -> a
    maximumBy ((ScoreRecord -> POSIXTime)
    -> ScoreRecord -> ScoreRecord -> Ordering
    forall a b. Ord a => (b -> a) -> b -> b -> Ordering
    comparing ScoreRecord -> POSIXTime
    HighScore.getDate) [ScoreRecord]
    scoreRecords
          renderOutcome :: FK.Outcome -> AttrString
          renderOutcome Outcome
    outcome =
            let color :: Color
    color | Outcome
    outcome Outcome -> [Outcome] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Outcome]
    FK.deafeatOutcomes = Color
    Color.cVeryBadEvent
                      | Outcome
    outcome Outcome -> [Outcome] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Outcome]
    FK.victoryOutcomes = Color
    Color.cVeryGoodEvent
                      | Bool
    otherwise = Color
    Color.cNeutralEvent
                lastRemark :: Text
    lastRemark
                  | Outcome
    outcome Outcome -> Outcome -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Outcome
    lastOutcome = Text
    ""
                  | Outcome
    outcome Outcome -> [Outcome] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Outcome]
    FK.deafeatOutcomes = Text
    "(last suffered ending)"
                  | Outcome
    outcome Outcome -> [Outcome] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [Outcome]
    FK.victoryOutcomes = Text
    "(last achieved ending)"
                  | Bool
    otherwise = Text
    "(last seen ending)"
            in Text -> AttrString
    textToAS Text
    "Game over message when"
               AttrString -> AttrString -> AttrString
    <+:> (Color -> Text -> AttrString
    textFgToAS Color
    color (Text -> Text
    T.toTitle (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Outcome -> Text
    FK.nameOutcomePast Outcome
    outcome)
                     AttrString -> AttrString -> AttrString
    <+:> Text -> AttrString
    textToAS Text
    lastRemark)
               AttrString -> AttrString -> AttrString
    forall a. Semigroup a => a -> a -> a
    <> Text -> AttrString
    textToAS Text
    ":"
      return $! if isSquareFont propFont
                then EM.singleton squareFont  -- single column, single font
                     $ xtranslateOverlay 2 $ offsetOverlay
                     $ concatMap snd $ blurb ++ blurbEnd
                else EM.unionWith (++)
                     (EM.map (xtranslateOverlay 1)
                      $ attrLinesToFontMap blurb)
                     (EM.map (xtranslateOverlay $ rwidth + 1)
                      $ attrLinesToFontMap blurbEnd)

    pickNumber :: MonadClientUI m => Bool -> Int -> m (Either MError Int)
    pickNumber :: forall (m :: * -> *).
    MonadClientUI m =>
    Bool -> Int -> m (Either MError Int)
    pickNumber Bool
    askNumber Int
    kAll = Bool -> m (Either MError Int) -> m (Either MError Int)
    forall a. HasCallStack => Bool -> a -> a
    assert (Int
    kAll Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    1) (m (Either MError Int) -> m (Either MError Int))
    -> m (Either MError Int) -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ do
      let shownKeys :: [KM]
    shownKeys = [ KM
    K.returnKM, KM
    K.spaceKM, Char -> KM
    K.mkChar Char
    '+', Char -> KM
    K.mkChar Char
    '-'
                      , KM
    K.backspaceKM, KM
    K.escKM ]
          frontKeyKeys :: [KM]
    frontKeyKeys = [KM]
    shownKeys [KM] -> [KM] -> [KM]
    forall a. [a] -> [a] -> [a]
    ++ (Char -> KM) -> String -> [KM]
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> KM
    K.mkChar [Char
    '0'..Char
    '9']
          gatherNumber :: Int -> m (Either MError Int)
    gatherNumber Int
    kCur = Bool -> m (Either MError Int) -> m (Either MError Int)
    forall a. HasCallStack => Bool -> a -> a
    assert (Int
    1 Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    kCur Bool -> Bool -> Bool
    && Int
    kCur Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    kAll) (m (Either MError Int) -> m (Either MError Int))
    -> m (Either MError Int) -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ do
            let kprompt :: Text
    kprompt = Text
    "Choose number:" Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    kCur
            MsgClassShow -> Text -> m ()
    forall (m :: * -> *) a.
    (MonadClientUI m, MsgShared a) =>
    a -> Text -> m ()
    msgAdd MsgClassShow
    MsgPromptGeneric Text
    kprompt
            sli <- [KM] -> m Slideshow
    forall (m :: * -> *). MonadClientUI m => [KM] -> m Slideshow
    reportToSlideshow [KM]
    shownKeys
            ekkm <- displayChoiceScreen "" ColorFull False sli frontKeyKeys
            case ekkm of
              Left KM
    kkm ->
                case KM -> Key
    K.key KM
    kkm of
                  K.Char Char
    '+' ->
                    Int -> m (Either MError Int)
    gatherNumber (Int -> m (Either MError Int)) -> Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ if Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1 Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    kAll then Int
    1 else Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1
                  K.Char Char
    '-' ->
                    Int -> m (Either MError Int)
    gatherNumber (Int -> m (Either MError Int)) -> Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ if Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1 Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    1 then Int
    kAll else Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1
                  K.Char Char
    l | Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    10 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Char -> Int
    Char.digitToInt Char
    l Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    kAll ->
                    Int -> m (Either MError Int)
    gatherNumber (Int -> m (Either MError Int)) -> Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ if Char -> Int
    Char.digitToInt Char
    l Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0
                                   then Int
    kAll
                                   else Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    min Int
    kAll (Char -> Int
    Char.digitToInt Char
    l)
                  K.Char Char
    l -> Int -> m (Either MError Int)
    gatherNumber (Int -> m (Either MError Int)) -> Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ Int
    kCur Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    10 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Char -> Int
    Char.digitToInt Char
    l
                  Key
    K.BackSpace -> Int -> m (Either MError Int)
    gatherNumber (Int -> m (Either MError Int)) -> Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max Int
    1 (Int
    kCur Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `div` Int
    10)
                  Key
    K.Return -> Either MError Int -> m (Either MError Int)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either MError Int -> m (Either MError Int))
    -> Either MError Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ Int -> Either MError Int
    forall a b. b -> Either a b
    Right Int
    kCur
                  Key
    K.Esc -> FailOrCmd Int -> Either MError Int
    forall a. FailOrCmd a -> Either MError a
    weaveJust (FailOrCmd Int -> Either MError Int)
    -> m (FailOrCmd Int) -> m (Either MError Int)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Text -> m (FailOrCmd Int)
    forall (m :: * -> *) a. MonadClientUI m => Text -> m (FailOrCmd a)
    failWith Text
    "never mind"
                  Key
    K.Space -> Either MError Int -> m (Either MError Int)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either MError Int -> m (Either MError Int))
    -> Either MError Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ MError -> Either MError Int
    forall a b. a -> Either a b
    Left MError
    forall a. Maybe a
    Nothing
                  Key
    _ -> String -> m (Either MError Int)
    forall a. HasCallStack => String -> a
    error (String -> m (Either MError Int))
    -> String -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ String
    "unexpected key" String -> KM -> String
    forall v. Show v => String -> v -> String
    `showFailure` KM
    kkm
              Right MenuSlot
    slot -> String -> m (Either MError Int)
    forall a. HasCallStack => String -> a
    error (String -> m (Either MError Int))
    -> String -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ String
    "unexpected menu slot" String -> MenuSlot -> String
    forall v. Show v => String -> v -> String
    `showFailure` MenuSlot
    slot
      if Int
    kAll Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 Bool -> Bool -> Bool
    || Bool -> Bool
    not Bool
    askNumber
      then Either MError Int -> m (Either MError Int)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either MError Int -> m (Either MError Int))
    -> Either MError Int -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ Int -> Either MError Int
    forall a b. b -> Either a b
    Right Int
    kAll
      else do
        res <- Int -> m (Either MError Int)
    gatherNumber Int
    kAll
        case res of
          Right Int
    k | Int
    k Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0 -> String -> m (Either MError Int)
    forall a. HasCallStack => String -> a
    error (String -> m (Either MError Int))
    -> String -> m (Either MError Int)
    forall a b. (a -> b) -> a -> b
    $ String
    "" String -> (Either MError Int, Int) -> String
    forall v. Show v => String -> v -> String
    `showFailure` (Either MError Int
    res, Int
    kAll)
          Either MError Int
    _ -> Either MError Int -> m (Either MError Int)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Either MError Int
    res

    -- | Produces a textual description of the tile at a position.
    lookAtTile :: MonadClientUI m
               => Bool             -- ^ can be seen right now?
               -> Point            -- ^ position to describe
               -> LevelId          -- ^ level the position is at
               -> Maybe ActorId    -- ^ the actor that looks
               -> Maybe MU.Person  -- ^ grammatical person of the item(s), if any
               -> m (Text, Text, [(Int, MU.Part)])
    lookAtTile :: forall (m :: * -> *).
    MonadClientUI m =>
    Bool
    -> Point
    -> LevelId
    -> Maybe ActorId
    -> Maybe Person
    -> m (Text, Text, [(Int, Part)])
    lookAtTile Bool
    canSee Point
    p LevelId
    lidV Maybe ActorId
    maid Maybe Person
    mperson = do
      CCUI{coscreen=ScreenContent{rwidth}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      cops@COps{cotile, coplace} <- getsState scops
      side <- getsClient sside
      factionD <- getsState sfactionD
      mb <- getsState $ \State
    s -> (ActorId -> State -> Actor) -> State -> ActorId -> Actor
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ActorId -> State -> Actor
    getActorBody State
    s (ActorId -> Actor) -> Maybe ActorId -> Maybe Actor
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe ActorId
    maid
      lvl <- getLevel lidV
      saimMode <- getsSession saimMode
      embeds <- getsState $ getEmbedBag lidV p
      itemToF <- getsState $ flip itemToFull
      seps <- getsClient seps
      localTime <- getsState $ getLocalTime lidV
      getKind <- getsState $ flip getIidKind
      let inhabitants = Point -> Level -> [ActorId]
    posToAidsLvl Point
    p Level
    lvl
          detail = DetailLevel
    -> (AimMode -> DetailLevel) -> Maybe AimMode -> DetailLevel
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe DetailLevel
    DetailAll AimMode -> DetailLevel
    detailLevel Maybe AimMode
    saimMode
          aims = Maybe Int -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe Int -> Bool) -> Maybe Int -> Bool
    forall a b. (a -> b) -> a -> b
    $ (\Actor
    b -> Bool -> Actor -> Point -> Int -> COps -> Level -> Maybe Int
    makeLine Bool
    False Actor
    b Point
    p Int
    seps COps
    cops Level
    lvl) (Actor -> Maybe Int) -> Maybe Actor -> Maybe Int
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Maybe Actor
    mb
          tkid = Level
    lvl Level -> Point -> ContentId TileKind
    `at` Point
    p
          tile = ContentData TileKind -> ContentId TileKind -> TileKind
    forall a. ContentData a -> ContentId a -> a
    okind ContentData TileKind
    cotile ContentId TileKind
    tkid
          vis | TileKind -> Text
    TK.tname TileKind
    tile Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    == Text
    "unknown space" = Part
    "that is"
              | Bool -> Bool
    not ([ActorId] -> Bool
    forall a. [a] -> Bool
    null [ActorId]
    inhabitants)
                Bool -> Bool -> Bool
    && (Actor -> Point
    bpos (Actor -> Point) -> Maybe Actor -> Maybe Point
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe Actor
    mb) Maybe Point -> Maybe Point -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Point -> Maybe Point
    forall a. a -> Maybe a
    Just Point
    p = Part
    "the terrain here is"
              | Bool -> Bool
    not Bool
    canSee = Part
    "you remember"
              | Bool -> Bool
    not Bool
    aims = Part
    "you are aware of"  -- walkable path a proxy for in LOS
              | Bool
    otherwise = Part
    "you see"
          vperson = case Maybe Person
    mperson of
            Maybe Person
    Nothing -> Part
    vis
            Just Person
    MU.Sg1st -> String -> Part
    forall a. HasCallStack => String -> a
    error String
    "an item speaks in first person"
            Just Person
    MU.Sg3rd -> Part
    "It is laying on"
            Just Person
    MU.PlEtc -> Part
    "They lay on"
          tilePart = Part -> Part
    MU.AW (Part -> Part) -> Part -> Part
    forall a b. (a -> b) -> a -> b
    $ Text -> Part
    MU.Text (Text -> Part) -> Text -> Part
    forall a b. (a -> b) -> a -> b
    $ TileKind -> Text
    TK.tname TileKind
    tile
          entrySentence ContentId PlaceKind
    pk Part
    blurb =
            [Part] -> Text
    makeSentence [Part
    blurb, Text -> Part
    MU.Text (Text -> Part) -> Text -> Part
    forall a b. (a -> b) -> a -> b
    $ PlaceKind -> Text
    PK.pname (PlaceKind -> Text) -> PlaceKind -> Text
    forall a b. (a -> b) -> a -> b
    $ ContentData PlaceKind -> ContentId PlaceKind -> PlaceKind
    forall a. ContentData a -> ContentId a -> a
    okind ContentData PlaceKind
    coplace ContentId PlaceKind
    pk]
          placeBlurb = case Point -> EntryMap -> Maybe PlaceEntry
    forall k a. Enum k => k -> EnumMap k a -> Maybe a
    EM.lookup Point
    p (EntryMap -> Maybe PlaceEntry) -> EntryMap -> Maybe PlaceEntry
    forall a b. (a -> b) -> a -> b
    $ Level -> EntryMap
    lentry Level
    lvl of
            Maybe PlaceEntry
    Nothing -> Text
    ""
            Just (PK.PEntry ContentId PlaceKind
    pk) -> ContentId PlaceKind -> Part -> Text
    entrySentence ContentId PlaceKind
    pk Part
    "it is an entrance to"
            Just (PK.PAround ContentId PlaceKind
    pk) -> ContentId PlaceKind -> Part -> Text
    entrySentence ContentId PlaceKind
    pk Part
    "it surrounds"
            Just (PK.PExists ContentId PlaceKind
    _) -> Text
    ""
          embedLook (ItemId
    iid, kit :: ItemQuant
    kit@(Int
    k, ItemTimers
    _)) =
            let itemFull :: ItemFull
    itemFull = ItemId -> ItemFull
    itemToF ItemId
    iid
                nWs :: Part
    nWs = DetailLevel
    -> Int
    -> FactionId
    -> EnumMap FactionId Faction
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsDetail DetailLevel
    detail
                                       Int
    rwidth FactionId
    side EnumMap FactionId Faction
    factionD Int
    k Time
    localTime ItemFull
    itemFull ItemQuant
    kit
            in (Int
    k, Part
    nWs)
          embedKindList =
            ((ItemId, ItemQuant) -> (ItemKind, (ItemId, ItemQuant)))
    -> [(ItemId, ItemQuant)] -> [(ItemKind, (ItemId, ItemQuant))]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(ItemId
    iid, ItemQuant
    kit) -> (ItemId -> ItemKind
    getKind ItemId
    iid, (ItemId
    iid, ItemQuant
    kit))) (ItemBag -> [(ItemId, ItemQuant)]
    forall k a. Enum k => EnumMap k a -> [(k, a)]
    EM.assocs ItemBag
    embeds)
          embedList = ((ItemId, ItemQuant) -> (Int, Part))
    -> [(ItemId, ItemQuant)] -> [(Int, Part)]
    forall a b. (a -> b) -> [a] -> [b]
    map (ItemId, ItemQuant) -> (Int, Part)
    embedLook ([(ItemId, ItemQuant)] -> [(Int, Part)])
    -> [(ItemId, ItemQuant)] -> [(Int, Part)]
    forall a b. (a -> b) -> a -> b
    $ COps
    -> ContentId TileKind
    -> [(ItemKind, (ItemId, ItemQuant))]
    -> [(ItemId, ItemQuant)]
    sortEmbeds COps
    cops ContentId TileKind
    tkid [(ItemKind, (ItemId, ItemQuant))]
    embedKindList
      return (makeSentence [vperson, tilePart], placeBlurb, embedList)

    -- | Produces a textual description of actors at a position.
    lookAtActors :: MonadClientUI m
                 => Point      -- ^ position to describe
                 -> LevelId    -- ^ level the position is at
                 -> m (Text, Maybe (MU.Part, Bool), Text)
    lookAtActors :: forall (m :: * -> *).
    MonadClientUI m =>
    Point -> LevelId -> m (Text, Maybe (Part, Bool), Text)
    lookAtActors Point
    p LevelId
    lidV = do
      CCUI{coscreen=ScreenContent{rwidth}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      side <- getsClient sside
      inhabitants <- getsState $ posToAidAssocs p lidV
      factionD <- getsState sfactionD
      localTime <- getsState $ getLocalTime lidV
      saimMode <- getsSession saimMode
      let detail = DetailLevel
    -> (AimMode -> DetailLevel) -> Maybe AimMode -> DetailLevel
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe DetailLevel
    DetailAll AimMode -> DetailLevel
    detailLevel Maybe AimMode
    saimMode
      case inhabitants of
        [] -> (Text, Maybe (Part, Bool), Text)
    -> m (Text, Maybe (Part, Bool), Text)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text
    "", Maybe (Part, Bool)
    forall a. Maybe a
    Nothing, Text
    "")
        (ActorId
    aid, Actor
    body) : [(ActorId, Actor)]
    rest -> do
          actorPronoun <- ActorId -> m Part
    forall (m :: * -> *). MonadClientUI m => ActorId -> m Part
    partPronounLeader ActorId
    aid
          itemFull <- getsState $ itemToFull $ btrunk body
          guardVerbs <- getsState $ guardItemVerbs body
          subjects <- mapM (partActorLeader . fst) inhabitants
          let bfact = EnumMap FactionId Faction
    factionD EnumMap FactionId Faction -> FactionId -> Faction
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! Actor -> FactionId
    bfid Actor
    body
              -- No "a" prefix even if singular and inanimate, to distinguish
              -- from items lying on the floor (and to simplify code).
              (subject, person) = squashedWWandW subjects
              resideVerb = case Actor -> Watchfulness
    bwatch Actor
    body of
                Watchfulness
    WWatch -> Part
    "be here"
                WWait Int
    0 -> Part
    "idle here"
                WWait Int
    _ -> Part
    "brace for impact"
                Watchfulness
    WSleep -> Part
    "sleep here"
                Watchfulness
    WWake -> Part
    "be waking up"
              flyVerb | Actor -> Bool
    bproj Actor
    body = Part
    "zip through here"
                      | Maybe ([Vector], Speed) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe ([Vector], Speed) -> Bool)
    -> Maybe ([Vector], Speed) -> Bool
    forall a b. (a -> b) -> a -> b
    $ Actor -> Maybe ([Vector], Speed)
    btrajectory Actor
    body = Part
    "move through here"
                      | Bool
    otherwise = Part
    resideVerb
              verbs = Part
    flyVerb Part -> [Part] -> [Part]
    forall a. a -> [a] -> [a]
    : [Part]
    guardVerbs
              projDesc | Bool -> Bool
    not (Actor -> Bool
    bproj Actor
    body) Bool -> Bool -> Bool
    || DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    < DetailLevel
    DetailHigh = Text
    ""
                       | Bool
    otherwise =
                let kit :: ItemQuant
    kit = Actor -> ItemBag
    beqp Actor
    body ItemBag -> ItemId -> ItemQuant
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! Actor -> ItemId
    btrunk Actor
    body
                    ps :: [Part]
    ps = [Int
    -> FactionId
    -> EnumMap FactionId Faction
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemMediumAW Int
    rwidth FactionId
    side EnumMap FactionId Faction
    factionD Time
    localTime
                                           ItemFull
    itemFull ItemQuant
    kit]
                    tailWords :: [Part] -> [Text]
    tailWords = [Text] -> [Text]
    forall a. HasCallStack => [a] -> [a]
    tail ([Text] -> [Text]) -> ([Part] -> [Text]) -> [Part] -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> [Text]
    T.words (Text -> [Text]) -> ([Part] -> Text) -> [Part] -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Part] -> Text
    makePhrase
                in if [Part] -> [Text]
    tailWords [Part]
    ps [Text] -> [Text] -> Bool
    forall a. Eq a => a -> a -> Bool
    == [Part] -> [Text]
    tailWords [Part]
    subjects
                   then Text
    ""
                   else [Part] -> Text
    makeSentence ([Part] -> Text) -> [Part] -> Text
    forall a b. (a -> b) -> a -> b
    $ Part
    "this is" Part -> [Part] -> [Part]
    forall a. a -> [a] -> [a]
    : [Part]
    ps
              factDesc = case Item -> Maybe FactionId
    jfid (Item -> Maybe FactionId) -> Item -> Maybe FactionId
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> Item
    itemBase ItemFull
    itemFull of
                Just FactionId
    tfid | FactionId
    tfid FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Actor -> FactionId
    bfid Actor
    body ->
                  let dominatedBy :: Text
    dominatedBy = if Actor -> FactionId
    bfid Actor
    body FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId
    side then Text
    "us" else Faction -> Text
    gname Faction
    bfact
                      tfact :: Faction
    tfact = EnumMap FactionId Faction
    factionD EnumMap FactionId Faction -> FactionId -> Faction
    forall k a. Enum k => EnumMap k a -> k -> a
    EM.! FactionId
    tfid
                  in Text
    "Originally of" Text -> Text -> Text
    <+> Faction -> Text
    gname Faction
    tfact
                     Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ", now fighting for" Text -> Text -> Text
    <+> Text
    dominatedBy Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."
                Maybe FactionId
    _ | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    < DetailLevel
    DetailHigh -> Text
    ""  -- only domination worth spamming
                Maybe FactionId
    _ | Actor -> FactionId
    bfid Actor
    body FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId
    side -> Text
    ""  -- just one of us
                Maybe FactionId
    _ | Actor -> Bool
    bproj Actor
    body -> Text
    "Launched by" Text -> Text -> Text
    <+> Faction -> Text
    gname Faction
    bfact Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."
                Maybe FactionId
    _ -> Text
    "One of" Text -> Text -> Text
    <+> Faction -> Text
    gname Faction
    bfact Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."
              idesc = if DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    < DetailLevel
    DetailHigh
                      then Text
    ""
                      else ItemKind -> Text
    IK.idesc (ItemKind -> Text) -> ItemKind -> Text
    forall a b. (a -> b) -> a -> b
    $ ItemFull -> ItemKind
    itemKind ItemFull
    itemFull
              -- If many different actors, only list names.
              sameTrunks = ((ActorId, Actor) -> Bool) -> [(ActorId, Actor)] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all (\(ActorId
    _, Actor
    b) -> Actor -> ItemId
    btrunk Actor
    b ItemId -> ItemId -> Bool
    forall a. Eq a => a -> a -> Bool
    == Actor -> ItemId
    btrunk Actor
    body) [(ActorId, Actor)]
    rest
              desc = Text -> Text
    wrapInParens (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Text
    projDesc Text -> Text -> Text
    <+> Text
    factDesc Text -> Text -> Text
    <+> Text
    idesc
              onlyIs = Actor -> Watchfulness
    bwatch Actor
    body Watchfulness -> Watchfulness -> Bool
    forall a. Eq a => a -> a -> Bool
    == Watchfulness
    WWatch Bool -> Bool -> Bool
    && [Part] -> Bool
    forall a. [a] -> Bool
    null [Part]
    guardVerbs
              allBlurb = [Part] -> Text
    makeSentence [Part -> Person -> Polarity -> Part -> [Part] -> Part
    MU.SubjectVVxV Part
    "and" Person
    person Polarity
    MU.Yes
                                                      Part
    subject [Part]
    verbs]
              headBlurb = [Part] -> Text
    makeSentence [Part -> Person -> Polarity -> Part -> [Part] -> Part
    MU.SubjectVVxV Part
    "and" Person
    MU.Sg3rd Polarity
    MU.Yes
                                                       ([Part] -> Part
    forall a. HasCallStack => [a] -> a
    head [Part]
    subjects) [Part]
    verbs]
              andProjectiles = case [Part]
    subjects of
                Part
    _ : projs :: [Part]
    projs@(Part
    _ : [Part]
    _) ->
                  let (Part
    subjectProjs, Person
    personProjs) =
                        [Part] -> (Part, Person)
    squashedWWandW [Part]
    projs
                  in [Part] -> Text
    makeSentence
                       [Person -> Polarity -> Part -> Part -> Part
    MU.SubjectVerb Person
    personProjs Polarity
    MU.Yes
                                       Part
    subjectProjs Part
    "can be seen"]
                [Part]
    _ -> Text
    ""
              actorAlive = Actor -> Int64
    bhp Actor
    body Int64 -> Int64 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int64
    0
              mactorPronounAlive =
                if Actor -> Bool
    bproj Actor
    body then Maybe (Part, Bool)
    forall a. Maybe a
    Nothing else (Part, Bool) -> Maybe (Part, Bool)
    forall a. a -> Maybe a
    Just (Part
    actorPronoun, Bool
    actorAlive)
          return $!
            if | not actorAlive && not (bproj body) ->
                 ( makeSentence
                     (MU.SubjectVerbSg (head subjects) "lie here"
                      : if null guardVerbs
                        then []
                        else [ MU.SubjectVVxV "and" MU.Sg3rd MU.No
                                              "and" guardVerbs
                             , "any more" ])
                 , mactorPronounAlive
                 , wrapInParens desc <+> andProjectiles )
               | sameTrunks ->  -- only non-proj or several similar projectiles
                 ( allBlurb
                 , mactorPronounAlive
                 , desc )
               | not (bproj body) && onlyIs ->
                 ( headBlurb
                 , mactorPronounAlive
                 , desc <+> andProjectiles )
               | not (bproj body) ->
                 ( makeSentence [subject, "can be seen"] <+> headBlurb
                 , mactorPronounAlive
                 , desc )
               | otherwise -> assert (bproj body && not (null rest))
                 ( makeSentence [subject, "can be seen"]
                 , Nothing
                 , "" )

    guardItemVerbs :: Actor -> State -> [MU.Part]
    guardItemVerbs :: Actor -> State -> [Part]
    guardItemVerbs Actor
    body State
    s =
      -- We only hint while, in reality, currently the client knows
      -- all the items in eqp of the foe. But we may remove the knowledge
      -- in the future and, anyway, it would require a dedicated
      -- UI mode beyond a couple of items per actor.
      let itemsSize :: Int
    itemsSize = Actor -> State -> Int
    guardItemSize Actor
    body State
    s
          belongingsVerbs :: [Part]
    belongingsVerbs | Int
    itemsSize Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 = [Part
    "fondle a trinket"]
                          | Int
    itemsSize Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1 = [Part
    "haul a hoard"]
                          | Bool
    otherwise = []
      in if Actor -> Bool
    bproj Actor
    body then [] else [Part]
    belongingsVerbs

    guardItemSize :: Actor -> State -> Int
    guardItemSize :: Actor -> State -> Int
    guardItemSize Actor
    body State
    s =
      let toReport :: ItemId -> Bool
    toReport ItemId
    iid =
            let itemKind :: ItemKind
    itemKind = ItemId -> State -> ItemKind
    getIidKind ItemId
    iid State
    s
            in Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    0 (GroupName ItemKind -> [(GroupName ItemKind, Int)] -> Maybe Int
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup GroupName ItemKind
    IK.UNREPORTED_INVENTORY (ItemKind -> [(GroupName ItemKind, Int)]
    IK.ifreq ItemKind
    itemKind)) Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0
      in [ItemId] -> Int
    forall a. [a] -> Int
    length ([ItemId] -> Int) -> [ItemId] -> Int
    forall a b. (a -> b) -> a -> b
    $ (ItemId -> Bool) -> [ItemId] -> [ItemId]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ItemId -> Bool
    toReport ([ItemId] -> [ItemId]) -> [ItemId] -> [ItemId]
    forall a b. (a -> b) -> a -> b
    $ ItemBag -> [ItemId]
    forall k a. Enum k => EnumMap k a -> [k]
    EM.keys (Actor -> ItemBag
    beqp Actor
    body)

    -- | Produces a textual description of items at a position.
    lookAtItems :: MonadClientUI m
                => Bool     -- ^ can be seen right now?
                -> Point    -- ^ position to describe
                -> LevelId  -- ^ level the position is at
                -> Maybe ActorId
                            -- ^ the actor that looks
                -> Maybe (MU.Part, Bool)
                            -- ^ pronoun for the big actor at the position, if any,
                            --   and whether the big actor is alive
                -> m (Text, Maybe MU.Person)
    lookAtItems :: forall (m :: * -> *).
    MonadClientUI m =>
    Bool
    -> Point
    -> LevelId
    -> Maybe ActorId
    -> Maybe (Part, Bool)
    -> m (Text, Maybe Person)
    lookAtItems Bool
    canSee Point
    p LevelId
    lidV Maybe ActorId
    maid Maybe (Part, Bool)
    mactorPronounAlive = do
      CCUI{coscreen=ScreenContent{rwidth}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      side <- getsClient sside
      itemToF <- getsState $ flip itemToFull
      mb <- getsState $ \State
    s -> (ActorId -> State -> Actor) -> State -> ActorId -> Actor
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ActorId -> State -> Actor
    getActorBody State
    s (ActorId -> Actor) -> Maybe ActorId -> Maybe Actor
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe ActorId
    maid
      -- Not using @viewedLevelUI@, because @aid@ may be temporarily not a leader.
      saimMode <- getsSession saimMode
      let standingOn = Point -> Maybe Point
    forall a. a -> Maybe a
    Just Point
    p Maybe Point -> Maybe Point -> Bool
    forall a. Eq a => a -> a -> Bool
    == (Actor -> Point
    bpos (Actor -> Point) -> Maybe Actor -> Maybe Point
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe Actor
    mb) Bool -> Bool -> Bool
    && LevelId -> Maybe LevelId
    forall a. a -> Maybe a
    Just LevelId
    lidV Maybe LevelId -> Maybe LevelId -> Bool
    forall a. Eq a => a -> a -> Bool
    == (Actor -> LevelId
    blid (Actor -> LevelId) -> Maybe Actor -> Maybe LevelId
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe Actor
    mb)
          -- In exploration mode the detail level depends on whether the actor
          -- that looks stand over the items, because then he can check details
          -- with inventory commands (or look in aiming mode).
          detailExploration = if Bool
    standingOn Bool -> Bool -> Bool
    && FactionId -> Maybe FactionId
    forall a. a -> Maybe a
    Just FactionId
    side Maybe FactionId -> Maybe FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == (Actor -> FactionId
    bfid (Actor -> FactionId) -> Maybe Actor -> Maybe FactionId
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe Actor
    mb)
                              then DetailLevel
    DetailLow
                              else DetailLevel
    DetailAll
          detail = DetailLevel
    -> (AimMode -> DetailLevel) -> Maybe AimMode -> DetailLevel
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe DetailLevel
    detailExploration AimMode -> DetailLevel
    detailLevel Maybe AimMode
    saimMode
      localTime <- getsState $ getLocalTime lidV
      is <- getsState $ getFloorBag lidV p
      factionD <- getsState sfactionD
      globalTime <- getsState stime
      getKind <- getsState $ flip getIidKindId
      mLeader <- case maid of
        Just ActorId
    aid | Bool
    standingOn -> do
          leaderPronoun <- ActorId -> m Part
    forall (m :: * -> *). MonadClientUI m => ActorId -> m Part
    partPronounLeader ActorId
    aid
          return $ Just (leaderPronoun, (bhp <$> mb) >= Just 0)
        Maybe ActorId
    _ -> Maybe (Part, Bool) -> m (Maybe (Part, Bool))
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe (Part, Bool)
    forall a. Maybe a
    Nothing
      let mactorPronounAliveLeader = Maybe (Part, Bool)
    mactorPronounAlive Maybe (Part, Bool) -> Maybe (Part, Bool) -> Maybe (Part, Bool)
    forall a. Maybe a -> Maybe a -> Maybe a
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> Maybe (Part, Bool)
    mLeader
      (subject, verb) <- case mactorPronounAliveLeader of
        Just (Part
    actorPronoun, Bool
    actorAlive) ->
          (Part, Text) -> m (Part, Text)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Part
    actorPronoun, if Bool
    actorAlive then Text
    "stand over" else Text
    "fall over")
        Maybe (Part, Bool)
    Nothing -> case Maybe ActorId
    maid of
          Just ActorId
    aid -> do
            subjectAid <- ActorId -> m Part
    forall (m :: * -> *). MonadClientUI m => ActorId -> m Part
    partActorLeader ActorId
    aid
            return (subjectAid, if canSee then "notice" else "remember")
          Maybe ActorId
    Nothing ->
            (Part, Text) -> m (Part, Text)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Part
    "one", if Bool
    canSee then Text
    "can see" else Text
    "may remember")
      let nWs (ItemId
    iid, kit :: ItemQuant
    kit@(Int
    k, ItemTimers
    _)) =
            DetailLevel
    -> Int
    -> FactionId
    -> EnumMap FactionId Faction
    -> Int
    -> Time
    -> ItemFull
    -> ItemQuant
    -> Part
    partItemWsDetail DetailLevel
    detail
                             Int
    rwidth FactionId
    side EnumMap FactionId Faction
    factionD Int
    k Time
    localTime (ItemId -> ItemFull
    itemToF ItemId
    iid) ItemQuant
    kit
          (object, person) = case EM.assocs is of
            (ItemId, ItemQuant)
    ii : (ItemId, ItemQuant)
    _ : (ItemId, ItemQuant)
    _ : [(ItemId, ItemQuant)]
    _ | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    <= DetailLevel
    DetailMedium ->
              ([Part] -> Part
    MU.Phrase [(ItemId, ItemQuant) -> Part
    nWs (ItemId, ItemQuant)
    ii, Part
    "and other items"], Person
    MU.PlEtc)
            [ii :: (ItemId, ItemQuant)
    ii@(ItemId
    _, (Int
    1, ItemTimers
    _))] -> ((ItemId, ItemQuant) -> Part
    nWs (ItemId, ItemQuant)
    ii, Person
    MU.Sg3rd)
            [(ItemId, ItemQuant)]
    iis -> ([Part] -> Part
    MU.WWandW ([Part] -> Part) -> [Part] -> Part
    forall a b. (a -> b) -> a -> b
    $ ((ItemId, ItemQuant) -> Part) -> [(ItemId, ItemQuant)] -> [Part]
    forall a b. (a -> b) -> [a] -> [b]
    map (ItemId, ItemQuant) -> Part
    nWs ([(ItemId, ItemQuant)] -> [Part])
    -> [(ItemId, ItemQuant)] -> [Part]
    forall a b. (a -> b) -> a -> b
    $ ((ItemId, ItemQuant) -> ContentId ItemKind)
    -> [(ItemId, ItemQuant)] -> [(ItemId, ItemQuant)]
    forall b a. Ord b => (a -> b) -> [a] -> [a]
    sortOn (ItemId -> ContentId ItemKind
    getKind (ItemId -> ContentId ItemKind)
    -> ((ItemId, ItemQuant) -> ItemId)
    -> (ItemId, ItemQuant)
    -> ContentId ItemKind
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ItemId, ItemQuant) -> ItemId
    forall a b. (a, b) -> a
    fst) [(ItemId, ItemQuant)]
    iis, Person
    MU.PlEtc)
      -- Here @squashedWWandW@ is not needed, because identical items at the same
      -- position are already merged in the floor item bag and multiple identical
      -- messages concerning different positions are merged with <x7>
      -- to distinguish from a stack of items at a single position.
      return ( if EM.null is || globalTime == timeZero
               then ""
               else makeSentence [MU.SubjectVerbSg subject (MU.Text verb), object]
             , if isNothing mactorPronounAlive then Just person else Nothing )

    lookAtStash :: MonadClientUI m => Point -> LevelId -> m Text
    lookAtStash :: forall (m :: * -> *). MonadClientUI m => Point -> LevelId -> m Text
    lookAtStash Point
    p LevelId
    lidV = do
      side <- (StateClient -> FactionId) -> m FactionId
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> FactionId
    sside
      factionD <- getsState sfactionD
      let locateStash (FactionId
    fid, Faction
    fact) = case Faction -> Maybe (LevelId, Point)
    gstash Faction
    fact of
            Just (LevelId
    lid, Point
    pos) | LevelId
    lid LevelId -> LevelId -> Bool
    forall a. Eq a => a -> a -> Bool
    == LevelId
    lidV  Bool -> Bool -> Bool
    && Point
    pos Point -> Point -> Bool
    forall a. Eq a => a -> a -> Bool
    == Point
    p ->
              Text -> Maybe Text
    forall a. a -> Maybe a
    Just (Text -> Maybe Text) -> Text -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ if FactionId
    fid FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    == FactionId
    side
                     then Text
    "Here is the shared inventory stash of your team."
                     else Faction -> Text
    gname Faction
    fact
                          Text -> Text -> Text
    <+> Text
    "set up their shared inventory stash here."
            Maybe (LevelId, Point)
    _ -> Maybe Text
    forall a. Maybe a
    Nothing
      return $! T.intercalate " " $ mapMaybe locateStash $ EM.assocs factionD

    -- | Produces a textual description of everything at the requested
    -- level's position.
    lookAtPosition :: MonadClientUI m
                   => Point -> LevelId -> m [(MsgClassShow, Text)]
    lookAtPosition :: forall (m :: * -> *).
    MonadClientUI m =>
    Point -> LevelId -> m [(MsgClassShow, Text)]
    lookAtPosition Point
    p LevelId
    lidV = do
      COps{cotile} <- (State -> COps) -> m COps
    forall a. (State -> a) -> m a
    forall (m :: * -> *) a. MonadStateRead m => (State -> a) -> m a
    getsState State -> COps
    scops
      side <- getsClient sside
      per <- getPerFid lidV
      let canSee = Point -> EnumSet Point -> Bool
    forall k. Enum k => k -> EnumSet k -> Bool
    ES.member Point
    p (Perception -> EnumSet Point
    totalVisible Perception
    per)
      (actorsBlurb, mactorPronounAlive, actorsDesc) <- lookAtActors p lidV
      mleader <- getsClient sleader
      (itemsBlurb, mperson) <-
        lookAtItems canSee p lidV mleader mactorPronounAlive
      let tperson = if Text -> Bool
    T.null Text
    itemsBlurb then Maybe Person
    forall a. Maybe a
    Nothing else Maybe Person
    mperson
      (tileBlurb, placeBlurb, embedsList) <-
        lookAtTile canSee p lidV mleader tperson
      inhabitants <- getsState $ posToAidAssocs p lidV
      let actorMsgClass =
            if (Actor -> FactionId
    bfid (Actor -> FactionId)
    -> ((ActorId, Actor) -> Actor) -> (ActorId, Actor) -> FactionId
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ActorId, Actor) -> Actor
    forall a b. (a, b) -> b
    snd ((ActorId, Actor) -> FactionId)
    -> [(ActorId, Actor)] -> [FactionId]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [(ActorId, Actor)]
    inhabitants) [FactionId] -> [FactionId] -> Bool
    forall a. Eq a => a -> a -> Bool
    == [FactionId
    side]
            then MsgClassShow
    MsgPromptGeneric  -- our single proj or non-proj; tame
            else MsgClassShow
    MsgPromptActors
      stashBlurb <- lookAtStash p lidV
      lvl@Level{lsmell, ltime} <- getLevel lidV
      saimMode <- getsSession saimMode
      let detail = DetailLevel
    -> (AimMode -> DetailLevel) -> Maybe AimMode -> DetailLevel
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe DetailLevel
    DetailAll AimMode -> DetailLevel
    detailLevel Maybe AimMode
    saimMode
          smellBlurb = case Point -> SmellMap -> Maybe Time
    forall k a. Enum k => k -> EnumMap k a -> Maybe a
    EM.lookup Point
    p SmellMap
    lsmell of
            Just Time
    sml | Time
    sml Time -> Time -> Bool
    forall a. Ord a => a -> a -> Bool
    > Time
    ltime ->
              let Delta Time
    t = Delta Time
    smellTimeout Delta Time -> Delta Time -> Delta Time
    `timeDeltaSubtract`
                              (Time
    sml Time -> Time -> Delta Time
    `timeDeltaToFrom` Time
    ltime)
                  seconds :: Int
    seconds = Time
    t Time -> Time -> Int
    `timeFitUp` Time
    timeSecond
              in Text
    "A smelly body passed here around" Text -> Text -> Text
    <+> Int -> Text
    forall a. Show a => a -> Text
    tshow Int
    seconds Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "s ago."
            Maybe Time
    _ -> Text
    ""
      embeds <- getsState $ getEmbedBag lidV p
      getKind <- getsState $ flip getIidKind
      let ppEmbedName :: (Int, MU.Part) -> Text
          ppEmbedName (Int
    k, Part
    part) =
            let verb :: Part
    verb = if Int
    k Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 then Part
    "is" else Part
    "are"
            in [Part] -> Text
    makeSentence [Part
    "There", Part
    verb, Part
    part]
          embedKindList = ((ItemId, ItemQuant) -> (ItemKind, (ItemId, ItemQuant)))
    -> [(ItemId, ItemQuant)] -> [(ItemKind, (ItemId, ItemQuant))]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(ItemId
    iid, ItemQuant
    kit) -> (ItemId -> ItemKind
    getKind ItemId
    iid, (ItemId
    iid, ItemQuant
    kit)))
                              (ItemBag -> [(ItemId, ItemQuant)]
    forall k a. Enum k => EnumMap k a -> [(k, a)]
    EM.assocs ItemBag
    embeds)
          feats = TileKind -> [Feature]
    TK.tfeature (TileKind -> [Feature]) -> TileKind -> [Feature]
    forall a b. (a -> b) -> a -> b
    $ ContentData TileKind -> ContentId TileKind -> TileKind
    forall a. ContentData a -> ContentId a -> a
    okind ContentData TileKind
    cotile (ContentId TileKind -> TileKind) -> ContentId TileKind -> TileKind
    forall a b. (a -> b) -> a -> b
    $ Level
    lvl Level -> Point -> ContentId TileKind
    `at` Point
    p
          tileActions = (Feature -> Maybe TileAction) -> [Feature] -> [TileAction]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe (Bool
    -> Bool
    -> [(ItemKind, (ItemId, ItemQuant))]
    -> Feature
    -> Maybe TileAction
    parseTileAction Bool
    False Bool
    False [(ItemKind, (ItemId, ItemQuant))]
    embedKindList)
                                 [Feature]
    feats
          isEmbedAction EmbedAction{} = Bool
    True
          isEmbedAction TileAction
    _ = Bool
    False
          embedVerb = [ Part
    "activated"
                      | (TileAction -> Bool) -> [TileAction] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any TileAction -> Bool
    isEmbedAction [TileAction]
    tileActions
                        Bool -> Bool -> Bool
    && ((ItemKind, (ItemId, ItemQuant)) -> Bool)
    -> [(ItemKind, (ItemId, ItemQuant))] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (\(ItemKind
    itemKind, (ItemId, ItemQuant)
    _) -> Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ [Effect] -> Bool
    forall a. [a] -> Bool
    null ([Effect] -> Bool) -> [Effect] -> Bool
    forall a b. (a -> b) -> a -> b
    $ ItemKind -> [Effect]
    IK.ieffects ItemKind
    itemKind)
                               [(ItemKind, (ItemId, ItemQuant))]
    embedKindList ]
          isToAction ToAction{} = Bool
    True
          isToAction TileAction
    _ = Bool
    False
          isWithAction WithAction{} = Bool
    True
          isWithAction TileAction
    _ = Bool
    False
          isEmptyWithAction (WithAction [] GroupName TileKind
    _) = Bool
    True
          isEmptyWithAction TileAction
    _ = Bool
    False
          alterVerb | (TileAction -> Bool) -> [TileAction] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any TileAction -> Bool
    isEmptyWithAction [TileAction]
    tileActions = [Part
    "very easily modified"]
                    | (TileAction -> Bool) -> [TileAction] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any TileAction -> Bool
    isToAction [TileAction]
    tileActions = [Part
    "easily modified"]
                    | (TileAction -> Bool) -> [TileAction] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any TileAction -> Bool
    isWithAction [TileAction]
    tileActions = [Part
    "potentially modified"]
                    | Bool
    otherwise = []
          verbs = [Part]
    embedVerb [Part] -> [Part] -> [Part]
    forall a. [a] -> [a] -> [a]
    ++ [Part]
    alterVerb
          alterBlurb = if [Part] -> Bool
    forall a. [a] -> Bool
    null [Part]
    verbs
                       then Text
    ""
                       else [Part] -> Text
    makeSentence [Part
    "can be", [Part] -> Part
    MU.WWandW [Part]
    verbs]
          toolFromAction (WithAction [(Int, GroupName ItemKind)]
    grps GroupName TileKind
    _) = [(Int, GroupName ItemKind)] -> Maybe [(Int, GroupName ItemKind)]
    forall a. a -> Maybe a
    Just [(Int, GroupName ItemKind)]
    grps
          toolFromAction TileAction
    _ = Maybe [(Int, GroupName ItemKind)]
    forall a. Maybe a
    Nothing
          toolsToAlterWith = (TileAction -> Maybe [(Int, GroupName ItemKind)])
    -> [TileAction] -> [[(Int, GroupName ItemKind)]]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe TileAction -> Maybe [(Int, GroupName ItemKind)]
    toolFromAction [TileAction]
    tileActions
          tItems = [[(Int, GroupName ItemKind)]] -> Text
    describeToolsAlternative [[(Int, GroupName ItemKind)]]
    toolsToAlterWith
          transformBlurb = if Text -> Bool
    T.null Text
    tItems
                           then Text
    ""
                           else Text
    "The following items on the ground or in equipment enable special transformations:"
                                Text -> Text -> Text
    <+> Text
    tItems Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "."  -- not telling to what terrain
          modifyBlurb = Text
    alterBlurb Text -> Text -> Text
    <+> Text
    transformBlurb
          midEOL = if DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    < DetailLevel
    DetailMedium
                      Bool -> Bool -> Bool
    || Text -> Bool
    T.null Text
    stashBlurb Bool -> Bool -> Bool
    && Text -> Bool
    T.null Text
    actorsDesc
                      Bool -> Bool -> Bool
    || Text -> Bool
    T.null Text
    smellBlurb Bool -> Bool -> Bool
    && Text -> Bool
    T.null Text
    itemsBlurb
                      Bool -> Bool -> Bool
    || [(Int, Part)] -> Bool
    forall a. [a] -> Bool
    null [(Int, Part)]
    embedsList Bool -> Bool -> Bool
    && Text -> Bool
    T.null Text
    modifyBlurb
                   then Text
    ""
                   else Text
    "\n"
          ms = [ (MsgClassShow
    MsgPromptAction, Text
    stashBlurb)
               , (MsgClassShow
    actorMsgClass, Text
    actorsBlurb)
               , (MsgClassShow
    MsgPromptGeneric, Text
    actorsDesc Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    midEOL) ]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(MsgClassShow
    MsgPromptGeneric, Text
    smellBlurb) | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailMedium]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(MsgClassShow
    MsgPromptItems, Text
    itemsBlurb Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    midEOL)]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(MsgClassShow
    MsgPromptFocus, Text
    tileBlurb) | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailMedium
                                                 Bool -> Bool -> Bool
    || DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Eq a => a -> a -> Bool
    == DetailLevel
    DetailLow
                                                    Bool -> Bool -> Bool
    && Bool -> Bool
    not ([(Int, Part)] -> Bool
    forall a. [a] -> Bool
    null [(Int, Part)]
    embedsList)]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(MsgClassShow
    MsgPromptGeneric, Text
    placeBlurb) | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailMedium]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ case DetailLevel
    detail of
                    DetailLevel
    DetailLow -> []  -- not to obscure aiming line
                    DetailLevel
    _ -> let n :: Int
    n = [Int] -> Int
    forall a. Num a => [a] -> a
    sum ([Int] -> Int) -> [Int] -> Int
    forall a b. (a -> b) -> a -> b
    $ ((Int, Part) -> Int) -> [(Int, Part)] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map (Int, Part) -> Int
    forall a b. (a, b) -> a
    fst [(Int, Part)]
    embedsList
                             wWandW :: Part
    wWandW = [Part] -> Part
    MU.WWandW ([Part] -> Part) -> [Part] -> Part
    forall a b. (a -> b) -> a -> b
    $ ((Int, Part) -> Part) -> [(Int, Part)] -> [Part]
    forall a b. (a -> b) -> [a] -> [b]
    map (Int, Part) -> Part
    forall a b. (a, b) -> b
    snd [(Int, Part)]
    embedsList
                         in [(MsgClassShow
    MsgPromptMention, (Int, Part) -> Text
    ppEmbedName (Int
    n, Part
    wWandW)) | Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0]
               [(MsgClassShow, Text)]
    -> [(MsgClassShow, Text)] -> [(MsgClassShow, Text)]
    forall a. [a] -> [a] -> [a]
    ++ [(MsgClassShow
    MsgPromptModify, Text
    modifyBlurb) | DetailLevel
    detail DetailLevel -> DetailLevel -> Bool
    forall a. Ord a => a -> a -> Bool
    >= DetailLevel
    DetailHigh]
      return $! if all (T.null . snd) ms && detail > DetailLow
                then [(MsgPromptFocus, tileBlurb)]
                else ms

    displayOneMenuItem :: MonadClientUI m
                       => (MenuSlot -> m OKX) -> [K.KM] -> Int -> MenuSlot
                       -> m K.KM
    displayOneMenuItem :: forall (m :: * -> *).
    MonadClientUI m =>
    (MenuSlot -> m OKX) -> [KM] -> Int -> MenuSlot -> m KM
    displayOneMenuItem MenuSlot -> m OKX
    renderOneItem [KM]
    extraKeys Int
    slotBound MenuSlot
    slot = do
      CCUI{coscreen=ScreenContent{rheight}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      let keys = [KM
    K.spaceKM, KM
    K.escKM]
                 [KM] -> [KM] -> [KM]
    forall a. [a] -> [a] -> [a]
    ++ [KM
    K.upKM | MenuSlot -> Int
    forall a. Enum a => a -> Int
    fromEnum MenuSlot
    slot Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0]
                 [KM] -> [KM] -> [KM]
    forall a. [a] -> [a] -> [a]
    ++ [KM
    K.downKM | MenuSlot -> Int
    forall a. Enum a => a -> Int
    fromEnum MenuSlot
    slot Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    slotBound]
                 [KM] -> [KM] -> [KM]
    forall a. [a] -> [a] -> [a]
    ++ [KM]
    extraKeys
      okx <- renderOneItem slot
      slides <- overlayToSlideshow (rheight - 2) keys okx
      km <- getConfirms ColorFull keys slides
      case K.key km of
        Key
    K.Up -> (MenuSlot -> m OKX) -> [KM] -> Int -> MenuSlot -> m KM
    forall (m :: * -> *).
    MonadClientUI m =>
    (MenuSlot -> m OKX) -> [KM] -> Int -> MenuSlot -> m KM
    displayOneMenuItem MenuSlot -> m OKX
    renderOneItem [KM]
    extraKeys Int
    slotBound (MenuSlot -> m KM) -> MenuSlot -> m KM
    forall a b. (a -> b) -> a -> b
    $ MenuSlot -> MenuSlot
    forall a. Enum a => a -> a
    pred MenuSlot
    slot
        Key
    K.Down -> (MenuSlot -> m OKX) -> [KM] -> Int -> MenuSlot -> m KM
    forall (m :: * -> *).
    MonadClientUI m =>
    (MenuSlot -> m OKX) -> [KM] -> Int -> MenuSlot -> m KM
    displayOneMenuItem MenuSlot -> m OKX
    renderOneItem [KM]
    extraKeys Int
    slotBound (MenuSlot -> m KM) -> MenuSlot -> m KM
    forall a b. (a -> b) -> a -> b
    $ MenuSlot -> MenuSlot
    forall a. Enum a => a -> a
    succ MenuSlot
    slot
        Key
    _ -> KM -> m KM
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return KM
    km

    okxItemLoreInline :: MonadClientUI m
                      => (ItemId -> ItemFull -> Int -> Text)
                      -> Int -> ItemDialogMode -> [(ItemId, ItemQuant)]
                      -> Int -> MenuSlot
                      -> m OKX
    okxItemLoreInline :: forall (m :: * -> *).
    MonadClientUI m =>
    (ItemId -> ItemFull -> Int -> Text)
    -> Int
    -> ItemDialogMode
    -> [(ItemId, ItemQuant)]
    -> Int
    -> MenuSlot
    -> m OKX
    okxItemLoreInline ItemId -> ItemFull -> Int -> Text
    promptFun Int
    meleeSkill ItemDialogMode
    dmode [(ItemId, ItemQuant)]
    iids Int
    widthRaw MenuSlot
    slot = do
      FontSetup{..} <- m FontSetup
    forall (m :: * -> *). MonadClientUI m => m FontSetup
    getFontSetup
      let (iid, kit@(k, _)) = iids !! fromEnum slot
          -- Some prop fonts are wider than mono (e.g., in dejavuBold font set),
          -- so the width in these artificial texts full of digits and strange
          -- characters needs to be smaller than @rwidth - 2@ that would suffice
          -- for mono.
          width = Int
    widthRaw Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    5
      itemFull <- getsState $ itemToFull iid
      (ovLab, ovDesc) <- itemDescOverlays True meleeSkill dmode iid kit itemFull
                                          width
      let prompt = ItemId -> ItemFull -> Int -> Text
    promptFun ItemId
    iid ItemFull
    itemFull Int
    k
          promptBlurb | Text -> Bool
    T.null Text
    prompt = []
                      | Bool
    otherwise = [AttrLine] -> Overlay
    offsetOverlay ([AttrLine] -> Overlay) -> [AttrLine] -> Overlay
    forall a b. (a -> b) -> a -> b
    $ Int -> Int -> AttrString -> [AttrLine]
    splitAttrString Int
    width Int
    width
                                    (AttrString -> [AttrLine]) -> AttrString -> [AttrLine]
    forall a b. (a -> b) -> a -> b
    $ Color -> Text -> AttrString
    textFgToAS Color
    Color.Brown (Text -> AttrString) -> Text -> AttrString
    forall a b. (a -> b) -> a -> b
    $ Text
    prompt Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "\n\n"
          len = Overlay -> Int
    forall a. [a] -> Int
    length Overlay
    promptBlurb
          descSym2 = Int -> Overlay -> Overlay
    ytranslateOverlay Int
    len Overlay
    ovLab
          descBlurb2 = Overlay
    promptBlurb Overlay -> Overlay -> Overlay
    forall a. [a] -> [a] -> [a]
    ++ Int -> Overlay -> Overlay
    ytranslateOverlay Int
    len Overlay
    ovDesc
          ov = (Overlay -> Overlay -> Overlay)
    -> DisplayFont
    -> Overlay
    -> EnumMap DisplayFont Overlay
    -> EnumMap DisplayFont Overlay
    forall k a.
    Enum k =>
    (a -> a -> a) -> k -> a -> EnumMap k a -> EnumMap k a
    EM.insertWith Overlay -> Overlay -> Overlay
    forall a. [a] -> [a] -> [a]
    (++) DisplayFont
    squareFont Overlay
    descSym2
               (EnumMap DisplayFont Overlay -> EnumMap DisplayFont Overlay)
    -> EnumMap DisplayFont Overlay -> EnumMap DisplayFont Overlay
    forall a b. (a -> b) -> a -> b
    $ DisplayFont -> Overlay -> EnumMap DisplayFont Overlay
    forall k a. Enum k => k -> a -> EnumMap k a
    EM.singleton DisplayFont
    propFont Overlay
    descBlurb2
      return (ov, [])

    okxItemLoreMsg :: MonadClientUI m
                   => (ItemId -> ItemFull -> Int -> Text)
                   -> Int -> ItemDialogMode -> [(ItemId, ItemQuant)]
                   -> MenuSlot
                   -> m OKX
    okxItemLoreMsg :: forall (m :: * -> *).
    MonadClientUI m =>
    (ItemId -> ItemFull -> Int -> Text)
    -> Int
    -> ItemDialogMode
    -> [(ItemId, ItemQuant)]
    -> MenuSlot
    -> m OKX
    okxItemLoreMsg ItemId -> ItemFull -> Int -> Text
    promptFun Int
    meleeSkill ItemDialogMode
    dmode [(ItemId, ItemQuant)]
    iids MenuSlot
    slot = do
      CCUI{coscreen=ScreenContent{rwidth}} <- (SessionUI -> CCUI) -> m CCUI
    forall a. (SessionUI -> a) -> m a
    forall (m :: * -> *) a. MonadClientUI m => (SessionUI -> a) -> m a
    getsSession SessionUI -> CCUI
    sccui
      FontSetup{..} <- getFontSetup
      let (iid, kit@(k, _)) = iids !! fromEnum slot
      itemFull <- getsState $ itemToFull iid
      (ovLab, ovDesc) <- itemDescOverlays True meleeSkill dmode iid kit itemFull
                                          rwidth
      let prompt = ItemId -> ItemFull -> Int -> Text
    promptFun ItemId
    iid ItemFull
    itemFull Int
    k
      msgAdd MsgPromptGeneric prompt
      let ov = (Overlay -> Overlay -> Overlay)
    -> DisplayFont
    -> Overlay
    -> EnumMap DisplayFont Overlay
    -> EnumMap DisplayFont Overlay
    forall k a.
    Enum k =>
    (a -> a -> a) -> k -> a -> EnumMap k a -> EnumMap k a
    EM.insertWith Overlay -> Overlay -> Overlay
    forall a. [a] -> [a] -> [a]
    (++) DisplayFont
    squareFont Overlay
    ovLab
               (EnumMap DisplayFont Overlay -> EnumMap DisplayFont Overlay)
    -> EnumMap DisplayFont Overlay -> EnumMap DisplayFont Overlay
    forall a b. (a -> b) -> a -> b
    $ DisplayFont -> Overlay -> EnumMap DisplayFont Overlay
    forall k a. Enum k => k -> a -> EnumMap k a
    EM.singleton DisplayFont
    propFont Overlay
    ovDesc
      return (ov, [])

    itemDescOverlays :: MonadClientUI m
                     => Bool -> Int -> ItemDialogMode -> ItemId -> ItemQuant
                     -> ItemFull -> Int
                     -> m (Overlay, Overlay)
    itemDescOverlays :: forall (m :: * -> *).
    MonadClientUI m =>
    Bool
    -> Int
    -> ItemDialogMode
    -> ItemId
    -> ItemQuant
    -> ItemFull
    -> Int
    -> m (Overlay, Overlay)
    itemDescOverlays Bool
    markParagraphs Int
    meleeSkill ItemDialogMode
    dmode ItemId
    iid ItemQuant
    kit ItemFull
    itemFull Int
    width = do
      FontSetup{squareFont} <- m FontSetup
    forall (m :: * -> *). MonadClientUI m => m FontSetup
    getFontSetup
      side <- getsClient sside
      arena <- getArenaUI
      localTime <- getsState $ getLocalTime arena
      factionD <- getsState sfactionD
      -- The hacky level 0 marks items never seen, but sent by server at gameover.
      jlid <- getsSession $ fromMaybe (toEnum 0) <$> EM.lookup iid . sitemUI
      let descAs = Int
    -> Bool
    -> FactionId
    -> EnumMap FactionId Faction
    -> Int
    -> ItemDialogMode
    -> Time
    -> LevelId
    -> ItemFull
    -> ItemQuant
    -> AttrString
    itemDesc Int
    width Bool
    markParagraphs FactionId
    side EnumMap FactionId Faction
    factionD Int
    meleeSkill
                            ItemDialogMode
    dmode Time
    localTime LevelId
    jlid ItemFull
    itemFull ItemQuant
    kit
      return $! labDescOverlay squareFont width descAs

    cycleLore :: MonadClientUI m => [m K.KM] -> [m K.KM] -> m ()
    cycleLore :: forall (m :: * -> *). MonadClientUI m => [m KM] -> [m KM] -> m ()
    cycleLore [m KM]
    _ [] = () -> m ()
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
    cycleLore [m KM]
    seen (m KM
    m : [m KM]
    rest) = do  -- @seen@ is needed for SPACE to end cycling
      km <- m KM
    m
      if | km == K.spaceKM -> cycleLore (m : seen) rest
         | km == K.mkChar '>' -> if null rest
                                 then cycleLore [] (reverse $ m : seen)
                                 else cycleLore (m : seen) rest
         | km == K.mkChar '<' -> case seen of
                                   m KM
    prev : [m KM]
    ps -> [m KM] -> [m KM] -> m ()
    forall (m :: * -> *). MonadClientUI m => [m KM] -> [m KM] -> m ()
    cycleLore [m KM]
    ps (m KM
    prev m KM -> [m KM] -> [m KM]
    forall a. a -> [a] -> [a]
    : m KM
    m m KM -> [m KM] -> [m KM]
    forall a. a -> [a] -> [a]
    : [m KM]
    rest)
                                   [] -> case [m KM] -> [m KM]
    forall a. [a] -> [a]
    reverse (m KM
    m m KM -> [m KM] -> [m KM]
    forall a. a -> [a] -> [a]
    : [m KM]
    rest) of
                                     m KM
    prev : [m KM]
    ps -> [m KM] -> [m KM] -> m ()
    forall (m :: * -> *). MonadClientUI m => [m KM] -> [m KM] -> m ()
    cycleLore [m KM]
    ps [m KM
    prev]
                                     [] -> String -> m ()
    forall a. HasCallStack => String -> a
    error String
    "cycleLore: screens disappeared"
         | km == K.escKM -> return ()
         | otherwise -> error "cycleLore: unexpected key"

    spoilsBlurb :: Text -> Int -> Int -> Text
    spoilsBlurb :: Text -> Int -> Int -> Text
    spoilsBlurb Text
    currencyName Int
    total Int
    dungeonTotal =
      if | Int
    dungeonTotal Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 ->
             Text
    "All the spoils of your team are of the practical kind."
         | Int
    total Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 -> Text
    "Your team haven't found any genuine treasure yet."
         | Bool
    otherwise -> [Part] -> Text
    makeSentence
             [ Part
    "your team's spoils are worth"
             , Int -> Part -> Part
    MU.CarAWs Int
    total (Part -> Part) -> Part -> Part
    forall a b. (a -> b) -> a -> b
    $ Text -> Part
    MU.Text Text
    currencyName
             , Part
    "out of the rumoured total"
             , Int -> Part
    MU.Cardinal Int
    dungeonTotal ]

    ppContainerWownW :: MonadClientUI m
                     => (ActorId -> m MU.Part) -> Bool -> Container -> m [MU.Part]
    ppContainerWownW :: forall (m :: * -> *).
    MonadClientUI m =>
    (ActorId -> m Part) -> Bool -> Container -> m [Part]
    ppContainerWownW ActorId -> m Part
    ownerFun Bool
    addPrepositions Container
    c = case Container
    c of
      CFloor{} -> [Part] -> m [Part]
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [Part
    "nearby"]
      CEmbed{} -> [Part] -> m [Part]
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [Part
    "embedded nearby"]
      CActor ActorId
    aid CStore
    store -> do
        side <- (StateClient -> FactionId) -> m FactionId
    forall a. (StateClient -> a) -> m a
    forall (m :: * -> *) a.
    MonadClientRead m =>
    (StateClient -> a) -> m a
    getsClient StateClient -> FactionId
    sside
        b <- getsState $ getActorBody aid
        owner <- ownerFun aid
        fidName <- getsState $ gname . (EM.! bfid b) . sfactionD
        let (preposition, noun) = ppCStore store
            prep = [Text -> Part
    MU.Text Text
    preposition | Bool
    addPrepositions]
        return $! prep ++ case store of
          CStore
    CGround -> Text -> Part
    MU.Text Text
    noun Part -> [Part] -> [Part]
    forall a. a -> [a] -> [a]
    : if Actor -> Bool
    bproj Actor
    b then [] else [Part
    "under", Part
    owner]
          CStore
    CStash -> if Actor -> FactionId
    bfid Actor
    b FactionId -> FactionId -> Bool
    forall a. Eq a => a -> a -> Bool
    /= FactionId
    side
                    then [Part -> Part -> Part
    MU.WownW (Text -> Part
    MU.Text Text
    fidName) (Text -> Part
    MU.Text Text
    noun)]
                    else [Text -> Part
    MU.Text Text
    noun]
          CStore
    _ -> [Part -> Part -> Part
    MU.WownW Part
    owner (Text -> Part
    MU.Text Text
    noun)]
      CTrunk{} -> String -> m [Part]
    forall a. HasCallStack => String -> a
    error (String -> m [Part]) -> String -> m [Part]
    forall a b. (a -> b) -> a -> b
    $ String
    "" String -> Container -> String
    forall v. Show v => String -> v -> String
    `showFailure` Container
    c

    nxtGameMode :: COps -> Int -> (ContentId MK.ModeKind, MK.ModeKind)
    nxtGameMode :: COps -> Int -> (ContentId ModeKind, ModeKind)
    nxtGameMode COps{ContentData ModeKind
    comode :: COps -> ContentData ModeKind
    comode :: ContentData ModeKind
    comode} Int
    snxtScenario =
      let f :: [(a, b)] -> p -> a -> b -> [(a, b)]
    f ![(a, b)]
    acc p
    _p !a
    i !b
    a = (a
    i, b
    a) (a, b) -> [(a, b)] -> [(a, b)]
    forall a. a -> [a] -> [a]
    : [(a, b)]
    acc
          campaignModes :: [(ContentId ModeKind, ModeKind)]
    campaignModes = ContentData ModeKind
    -> GroupName ModeKind
    -> ([(ContentId ModeKind, ModeKind)]
        -> Int
        -> ContentId ModeKind
        -> ModeKind
        -> [(ContentId ModeKind, ModeKind)])
    -> [(ContentId ModeKind, ModeKind)]
    -> [(ContentId ModeKind, ModeKind)]
    forall a b.
    ContentData a
    -> GroupName a -> (b -> Int -> ContentId a -> a -> b) -> b -> b
    ofoldlGroup' ContentData ModeKind
    comode GroupName ModeKind
    MK.CAMPAIGN_SCENARIO [(ContentId ModeKind, ModeKind)]
    -> Int
    -> ContentId ModeKind
    -> ModeKind
    -> [(ContentId ModeKind, ModeKind)]
    forall {a} {b} {p}. [(a, b)] -> p -> a -> b -> [(a, b)]
    f []
      in [(ContentId ModeKind, ModeKind)]
    campaignModes [(ContentId ModeKind, ModeKind)]
    -> Int -> (ContentId ModeKind, ModeKind)
    forall a. HasCallStack => [a] -> Int -> a
    !! (Int
    snxtScenario Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` [(ContentId ModeKind, ModeKind)] -> Int
    forall a. [a] -> Int
    length [(ContentId ModeKind, ModeKind)]
    campaignModes)