-- | The type of definitions of screen layout and features. module Game.LambdaHack.Client.UI.Content.Screen ( ScreenContent(..), emptyScreenContent, makeData #ifdef EXPOSE_INTERNAL -- * Internal operations , emptyScreenContentRaw, validateSingle #endif ) where import Prelude () import Game.LambdaHack.Core.Prelude import qualified Data.ByteString as BS import qualified Data.EnumMap.Strict as EM import qualified Data.Text as T import Game.LambdaHack.Content.ItemKind (ItemKind) import qualified Game.LambdaHack.Content.RuleKind as RK import Game.LambdaHack.Definition.Defs -- | Screen layout and features definition. -- -- Warning: this type is not abstract, but its values should not be -- created ad hoc, even for unit tests, but should be constructed -- with @makeData@, which includes validation, -- -- The @emptyScreenContent@ is one such valid by construction value -- of this type. It's suitable for bootstrapping and for testing. data ScreenContent = ScreenContent { rwidth :: X -- ^ screen width , rheight :: Y -- ^ screen height , rwebAddress :: String -- ^ an extra blurb line for the main menu , rintroScreen :: ([String], [[String]]) -- ^ the intro screen (first help screen) text -- and the rest of the manual , rapplyVerbMap :: EM.EnumMap (ContentSymbol ItemKind) T.Text -- ^ verbs to use for apply actions , rFontFiles :: [(FilePath, BS.ByteString)] -- ^ embedded game-supplied font files } emptyScreenContentRaw :: ScreenContent emptyScreenContentRaw = ScreenContent { rwidth = 5 , rheight = 5 , rwebAddress = "" , rintroScreen = ([], []) , rapplyVerbMap = EM.empty , rFontFiles = [] } emptyScreenContent :: ScreenContent emptyScreenContent = assert (null $ validateSingle RK.emptyRuleContent emptyScreenContentRaw) emptyScreenContentRaw -- | Catch invalid rule kind definitions. validateSingle :: RK.RuleContent -> ScreenContent -> [Text] validateSingle corule ScreenContent{..} = (let tsGt80 = filter ((> 80) . T.length) $ map T.pack [rwebAddress] in case tsGt80 of [] -> [] tGt80 : _ -> ["rwebAddress's length is over 80:" <> tGt80]) ++ (let tsGt41 = filter ((> 41) . T.length) $ map T.pack $ fst rintroScreen in case tsGt41 of [] -> [] tGt41 : _ -> ["intro screen has a line with length over 41:" <> tGt41]) ++ (let tsGt80 = filter ((> 80) . T.length) $ map T.pack $ intercalate [""] $ snd rintroScreen in case tsGt80 of [] -> [] tGt80 : _ -> ["manual has a line with length over 80:" <> tGt80]) -- The following reflect the only current UI implementation. ++ [ "rwidth /= RK.rWidthMax" | rwidth /= RK.rWidthMax corule ] ++ [ "rheight /= RK.rHeightMax + 3" | rheight /= RK.rHeightMax corule + 3] makeData :: RK.RuleContent -> ScreenContent -> ScreenContent makeData corule sc = let singleOffenders = validateSingle corule sc in assert (null singleOffenders `blame` "Screen Content not valid" `swith` singleOffenders) sc