module Game.LambdaHack.Content.ModeKind
( Caves, Roster(..), Player(..), ModeKind(..), LeaderMode(..), AutoLeader(..)
, Outcome(..), HiIndeterminant(..), HiCondPoly, HiSummand, HiPolynomial
, validateSingleModeKind, validateAllModeKind
) where
import Data.Binary
import qualified Data.EnumMap.Strict as EM
import qualified Data.IntMap.Strict as IM
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import qualified NLP.Miniutter.English as MU ()
import Game.LambdaHack.Common.Ability
import qualified Game.LambdaHack.Common.Dice as Dice
import Game.LambdaHack.Common.Misc
import Game.LambdaHack.Common.Msg
import Game.LambdaHack.Content.CaveKind
import Game.LambdaHack.Content.ItemKind (ItemKind)
data ModeKind = ModeKind
{ msymbol :: !Char
, mname :: !Text
, mfreq :: !(Freqs ModeKind)
, mroster :: !Roster
, mcaves :: !Caves
, mdesc :: !Text
}
deriving Show
type Caves = IM.IntMap (GroupName CaveKind, Maybe Bool)
data Roster = Roster
{ rosterList :: ![Player Dice.Dice]
, rosterEnemy :: ![(Text, Text)]
, rosterAlly :: ![(Text, Text)]
}
deriving (Show, Eq)
data Outcome =
Killed
| Defeated
| Camping
| Conquer
| Escape
| Restart
deriving (Show, Eq, Ord, Enum, Bounded, Generic)
instance Binary Outcome
data HiIndeterminant = HiConst | HiLoot | HiBlitz | HiSurvival | HiKill | HiLoss
deriving (Show, Eq, Ord, Generic)
instance Binary HiIndeterminant
type HiPolynomial = [(HiIndeterminant, Double)]
type HiSummand = (HiPolynomial, [Outcome])
type HiCondPoly = [HiSummand]
data Player a = Player
{ fname :: !Text
, fgroup :: !(GroupName ItemKind)
, fskillsOther :: !Skills
, fcanEscape :: !Bool
, fneverEmpty :: !Bool
, fhiCondPoly :: !HiCondPoly
, fhasNumbers :: !Bool
, fhasGender :: !Bool
, ftactic :: !Tactic
, fentryLevel :: !a
, finitialActors :: !a
, fleaderMode :: !LeaderMode
, fhasUI :: !Bool
}
deriving (Show, Eq, Ord, Generic)
instance Binary a => Binary (Player a)
data LeaderMode =
LeaderNull
| LeaderAI AutoLeader
| LeaderUI AutoLeader
deriving (Show, Eq, Ord, Generic)
instance Binary LeaderMode
data AutoLeader = AutoLeader
{ autoDungeon :: !Bool
, autoLevel :: !Bool
}
deriving (Show, Eq, Ord, Generic)
instance Binary AutoLeader
validateSingleModeKind :: ModeKind -> [Text]
validateSingleModeKind ModeKind{..} =
[ "mname longer than 20" | T.length mname > 20 ]
++ validateSingleRoster mcaves mroster
validateSingleRoster :: Caves -> Roster -> [Text]
validateSingleRoster caves Roster{..} =
[ "no player keeps the dungeon alive" | all (not . fneverEmpty) rosterList ]
++ concatMap (validateSinglePlayer caves) rosterList
++ let checkPl field pl =
[ pl <+> "is not a player name in" <+> field
| all ((/= pl) . fname) rosterList ]
checkDipl field (pl1, pl2) =
[ "self-diplomacy in" <+> field | pl1 == pl2 ]
++ checkPl field pl1
++ checkPl field pl2
in concatMap (checkDipl "rosterEnemy") rosterEnemy
++ concatMap (checkDipl "rosterAlly") rosterAlly
validateSinglePlayer :: Caves -> Player Dice.Dice -> [Text]
validateSinglePlayer caves Player{..} =
[ "fname empty:" <+> fname | T.null fname ]
++ [ "first word of fname longer than 15:" <+> fname
| T.length (head $ T.words fname) > 15 ]
++ [ "no UI client, but UI leader:" <+> fname
| not fhasUI && case fleaderMode of
LeaderUI _ -> True
_ -> False ]
++ [ "fentryLevel value not among cave numbers:" <+> fname
| any (`notElem` IM.keys caves)
[Dice.minDice fentryLevel
.. Dice.maxDice fentryLevel] ]
++ [ "fskillsOther not negative:" <+> fname
| any (>= 0) $ EM.elems fskillsOther ]
validateAllModeKind :: [ModeKind] -> [Text]
validateAllModeKind _ = []