{-# LANGUAGE TupleSections #-} -- | General content types and operations. module Game.LambdaHack.Common.Kind ( ContentData -- re-exported without some operations , COps(..) , emptyCOps , ItemSpeedup , getKindMean, speedupItem , okind, omemberGroup, oisSingletonGroup, ouniqGroup, opick , ofoldlWithKey', ofoldlGroup', omapVector, oimapVector , olength, linearInterpolation, emptyMultiGroupMode, emptyMultiGroupItem #ifdef EXPOSE_INTERNAL , emptyUnknownTile , emptyUIFactionGroupName #endif -- * Operations both internal and used in unit tests , emptyUIFaction ) where import Prelude () import Game.LambdaHack.Core.Prelude import qualified Data.Vector as V import qualified Game.LambdaHack.Common.ItemAspect as IA import qualified Game.LambdaHack.Common.Tile as Tile import qualified Game.LambdaHack.Content.CaveKind as CK 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.RuleKind as RK 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.ContentData import Game.LambdaHack.Definition.Defs import Game.LambdaHack.Definition.DefsInternal (GroupName (GroupName), toContentSymbol) import Game.LambdaHack.Definition.Flavour (dummyFlavour) -- | Operations for all content types, gathered together. -- -- 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@ for each particular content kind, which includes validation, -- and with @speedupItem@, etc., to ensure internal consistency. -- -- The @emptyCOps@ is one such valid by construction value of this type, -- except for the @cocave@ field. It's suitable for bootstrapping -- and for tests not involving dungeon generation from cave templates. data COps = COps { cocave :: ContentData CK.CaveKind -- server only , cofact :: ContentData FK.FactionKind , coitem :: ContentData IK.ItemKind , comode :: ContentData MK.ModeKind -- server only , coplace :: ContentData PK.PlaceKind -- server only, so far , corule :: RK.RuleContent , cotile :: ContentData TK.TileKind , coItemSpeedup :: ItemSpeedup , coTileSpeedup :: Tile.TileSpeedup } instance Show COps where show _ = "game content" instance Eq COps where (==) _ _ = True emptyMultiGroupItem :: IK.ItemKind emptyMultiGroupItem = IK.ItemKind { isymbol = toContentSymbol 'E' , iname = "emptyCOps item" , ifreq = map (, 1) $ IK.mandatoryGroups ++ IK.mandatoryGroupsSingleton , iflavour = [dummyFlavour] , icount = 0 , irarity = [] , iverbHit = "" , iweight = 0 , idamage = 0 , iaspects = [] , ieffects = [] , idesc = "" , ikit = [] } emptyUnknownTile :: TK.TileKind emptyUnknownTile = TK.TileKind -- needs to have index 0 and alter 1 { tsymbol = 'E' , tname = "unknown space" -- name checked in validation , tfreq = map (, 1) $ TK.mandatoryGroups ++ TK.mandatoryGroupsSingleton , tcolor = Color.BrMagenta , tcolor2 = Color.BrMagenta , talter = 1 , tfeature = [] } emptyUIFactionGroupName :: GroupName FK.FactionKind emptyUIFactionGroupName = GroupName "emptyUIFaction" emptyUIFaction :: FK.FactionKind emptyUIFaction = FK.FactionKind { fname = "emptyUIFaction" , ffreq = [(emptyUIFactionGroupName, 1)] , fteam = FK.TeamContinuity 999 -- must be > 0 , fgroups = [] , fskillsOther = Ability.zeroSkills , fcanEscape = False , fneverEmpty = True -- to keep the dungeon alive , fhiCondPoly = [] , fhasGender = False , finitDoctrine = Ability.TBlock , fspawnsFast = False , fhasPointman = False , fhasUI = True -- to own the UI frontend , finitUnderAI = False , fenemyTeams = [] , falliedTeams = [] } emptyMultiGroupMode :: MK.ModeKind emptyMultiGroupMode = MK.ModeKind { mname = "emptyMultiGroupMode" , mfreq = map (, 1) MK.mandatoryGroups , mtutorial = False , mattract = False , mroster = [(emptyUIFactionGroupName, [])] , mcaves = [] , mendMsg = [] , mrules = "" , mdesc = "" , mreason = "" , mhint = "" } -- | This is as empty, as possible, but still valid content, except for -- @cocave@ which is empty and not valid (making it valid would require -- bloating most other contents). emptyCOps :: COps emptyCOps = let corule = RK.emptyRuleContent coitem = IK.makeData (RK.ritemSymbols corule) [emptyMultiGroupItem] [] [] cotile = TK.makeData [emptyUnknownTile] [] [] cofact = FK.makeData [emptyUIFaction] [emptyUIFactionGroupName] [] in COps { cocave = emptyContentData -- not valid! beware when testing! -- to make valid cave content, we'd need to define a single cave kind, -- which involves creating and validating tile and place kinds, etc. , cofact , coitem , comode = MK.makeData cofact [emptyMultiGroupMode] [] [] , coplace = PK.makeData cotile [] [] [] , corule , cotile , coItemSpeedup = speedupItem coitem , coTileSpeedup = Tile.speedupTile False cotile } -- | Map from an item kind identifier to the mean aspect value for the kind. newtype ItemSpeedup = ItemSpeedup (V.Vector IA.KindMean) getKindMean :: ContentId IK.ItemKind -> ItemSpeedup -> IA.KindMean getKindMean kindId (ItemSpeedup is) = is V.! contentIdIndex kindId speedupItem :: ContentData IK.ItemKind -> ItemSpeedup speedupItem coitem = let f !kind = let kmMean = IA.meanAspect kind kmConst = not $ IA.aspectsRandom (IK.iaspects kind) in IA.KindMean{..} in ItemSpeedup $ omapVector coitem f