You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
temptress-bot/src/state/gamestate.ts

339 lines
14 KiB

export enum DieFace {
FAIL = 'F',
STOP = 'S',
SCORE_0 = '0',
SCORE_1 = '1',
SCORE_2 = '2',
SCORE_3 = '3',
SCORE_4 = '4',
SCORE_5 = '5',
WILD = '*',
HEART = '<3',
QUESTION = '?',
}
export enum DieState {
ROLLED = 'rolled',
SELECTED = 'selected',
HELD = 'held',
HELD_SELECTED = 'held_selected',
SCORED = 'scored',
}
export function isHeldState(d: DieState): d is DieState.HELD | DieState.HELD_SELECTED {
return d === DieState.HELD || d === DieState.HELD_SELECTED
}
export function isSelectedState(d: DieState): d is DieState.SELECTED | DieState.HELD_SELECTED {
return d === DieState.SELECTED || d === DieState.HELD_SELECTED
}
export function setSelected(d: DieState): DieState.SELECTED | DieState.HELD_SELECTED | DieState.SCORED {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.SELECTED:
case DieState.HELD_SELECTED:
case DieState.SCORED:
return d
}
}
export function setDeselected(d: DieState): DieState.ROLLED | DieState.HELD | DieState.SCORED {
switch (d) {
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.ROLLED:
case DieState.HELD:
case DieState.SCORED:
return d
}
}
export function toggleSelected(d: DieState): DieState {
switch (d) {
case DieState.ROLLED:
return DieState.SELECTED
case DieState.SELECTED:
return DieState.ROLLED
case DieState.HELD:
return DieState.HELD_SELECTED
case DieState.HELD_SELECTED:
return DieState.HELD
case DieState.SCORED:
return DieState.SCORED
}
}
export interface DieResult {
readonly type: DieType
readonly face: DieFace
readonly state: DieState
}
export interface DieType {
// The name of this die type
readonly name: string
// The faces of this die type; faces may be repeated to increase their odds
readonly faces: readonly DieFace[]
}
export interface AIWeights {
// How much this AI likes to choose actions that increase the damage bonus
// damageBonusWeight is static and does not change
readonly damageBonusWeight: number
// How much this AI likes to choose actions that increase the requirement of stop dice in a turn
// stopWeight is multiplied by the number of stop dice both players can gain
readonly stopWeight: number
// How much this AI likes to choose actions that decrease the limit of fail dice in a turn
// failWeight is multiplied by the number of fail dice both players can lose
readonly failWeight: number
// How much this AI likes to choose actions that are finishers for the opponent.
// finisherWeight is multiplied by the percentage of max damage the opponent has taken
readonly finisherWeight: number
// How much this AI dislikes choosing actions that are finishers for itself.
// selfFinisherWeight is multiplied by the percentage of max damage the AI has taken
// If this would reduce the weight of an action to 0 or less, that action has a weight of 0 and will only be
// selected randomly if all actions have a weight of 0.
readonly selfFinisherWeight: number
// Approximate lead that the AI would prefer to have before ending its turn when it has the option to keep pressing
readonly desiredLead: number
// Approximate damage behind the previous turn total that the AI is willing to take to play it safe,
// assuming doing so wouldn't be fatal (not enough damage to kill or not a finisher)
// When there's one fail die left, the AI will prioritize stopping as long as the damage it will be taking is within
// a randomly selected 50-150% of this value
readonly allowedDamage: number
// Integer from 0-100:
// 100 means AI keeps rolling until it has one fail die left before trying to stop
// 0 means AI always stops as soon as it has at least the desired lead
// Anything in between is the percentage chance of continuing, which is rolled once for each fail die remaining
// besides the last. If the chance fails once, then the AI uses that turn as if it were trying to stop.
// This roll is only performed when the AI already has the lead, is ahead by a value within an acceptable distance
// of the desired lead, and the AI has at least two additional fail dice remaining before failing.
readonly recklessnessPercent: number
// Integer from 0-100:
// 0 means AI never holds stops until it's ready to end its turn
// 100 means AI always holds stops it sees
// Anything in between is the percentage chance of locking a new stop die that appears, rolled once per stop die
// needed to end the turn per die that comes up stop. If the chance succeeds once, then the AI holds that stop die.
// This roll is only performed when the AI is not already stopping its turn.
readonly holdStopsPercent: number
}
export interface PlayerStartingState extends Pick<PlayerState, 'damageMax' | 'stopCount' | 'failCount'> {
// Percentage of damage that this player starts off dealing
readonly damageBonusBase: number
// Percentage of damage this player's damage increases by with each damage bonus earned
readonly damageBonusIncrement: number
// Maximum number of damage bonuses that this player can have, or 0 if there's no upper limit
readonly maxDamageBonuses: number
// Minimum number of dice required to stop
readonly minStopCount: number
// Minimum number of dice required to fail
readonly minFailCount: number
// Maximum number of dice required to stop
readonly maxStopCount: number
// Maximum number of dice required to fail
readonly maxFailCount: number
// Base amount of damage that must be dealt to recover, or 0 if recovery is forbidden
// Defaults to 1000
readonly recoverBase?: number
// Amount by which the amount needed to recover increases each time recovery is earned
// Defaults to 1000
readonly recoverIncrement?: number
// Percentage of current damage taken that is removed when recovery is earned
// Defaults to 50
readonly recoverPercent?: number
// Instructions for if this player is an AI player. If not specified, this player cannot be played by the AI.
readonly aiData?: AIWeights
}
export interface Difficulty {
// Name of the difficulty in the select menu
readonly name: string
// Short description of the difficulty in the select menu
readonly shortDescription?: string
// Description of the difficulty when selected
readonly description?: string
// Starting values for the top
readonly topStats: PlayerStartingState
// Starting values for the bottom
readonly bottomStats: PlayerStartingState
}
export interface PlayerText {
// Possible random names for this player
readonly names?: readonly string[]
// Text given at the start of the game from this side
readonly startText?: TriggeredText
// Text given at the end of the game from this side
readonly endText?: TriggeredText
// The name of the damage value for this player
readonly damage: string
// The name of the player's damage limit, defaulting to "Max " + damage
readonly maxDamage?: string
// The name of this player's stop count requirement
readonly stopCount: string
// The name of this player's fail count limit
readonly failCount: string
// The name of this player's incoming damage bonus
readonly damageBonus: string
}
export interface GameTheme {
readonly name: string
readonly shortDescription: string
readonly description: string
readonly actions: readonly GameAction[]
readonly difficulties: readonly Difficulty[]
readonly topText: PlayerText
readonly bottomText: PlayerText
}
export interface TriggeredText {
// The dialogues that can be triggered for this action.
// Dialogue can be disabled for characters run by human players.
readonly dialogue?: readonly string[]
// The descriptions that can be triggered for this action.
// Descriptions are always displayed, regardless of player.
readonly description?: readonly string[]
}
export interface ActionText {
// Name of the action in the select list for this side
readonly name: string
// Description of the action in the select list for this side
readonly shortDescription: string
// Description of the action when selected for this side
readonly description: string
// Text given when this side selects this action.
readonly selectAction?: TriggeredText
// Text given when the other side selects this action.
readonly opponentSelectsAction?: TriggeredText
// Text given when this side finishes a turn in this action without failing.
readonly passTurn?: TriggeredText
// Text given when this side receives a turn from the opponent in this action.
readonly opponentPassesTurn?: TriggeredText
// Text given when this side rerolls the dice for this action.
readonly reroll?: TriggeredText
// Text given when the opposing side rerolls the dice for this action.
readonly opponentRerolls?: TriggeredText
// Text given the first time each turn this side is one fail die away from failing
readonly aboutToFailTurn?: TriggeredText
// Text given the first time each turn the opposing side is one fail die away from failing
readonly opponentAboutToFailTurn?: TriggeredText
// Text given when this side fails the turn by accumulating fail dice
readonly failTurn?: TriggeredText
// Text given when the opposing side fails the turn by accumulating fail dice
readonly opponentFailsTurn?: TriggeredText
// Text given when this side fails the turn by stopping without enough points
readonly abandonTurn?: TriggeredText
// Text given when the opposing side fails the turn by stopping without enough points
readonly opponentAbandonsTurn?: TriggeredText
// Text given when this side is defeated by being pushed over their max damage with this action
readonly defeated?: TriggeredText
// Text given when this side defeats the opponent by pushing them over their max damage with this action
readonly opponentDefeated?: TriggeredText
}
export interface GameAction {
// The text from the top's perspective, and for the top's actions
readonly topText: ActionText
// The text from the bottom's perspective, and for the bottom's actions
readonly bottomText: ActionText
// The dice that are rolled for this action
readonly dice: readonly DieType[]
// True if the loser of this action increases the amount of damage they take
readonly givesDamageBonus: boolean
// True if the loser of this action has to get an additional stop die to end their turn
readonly givesStopCount: boolean
// True if the loser of this action has one fewer buffer for fail dice
readonly givesFailCount: boolean
// Whether this action can end the game in a loss for the top
readonly canFinishTop: boolean
// Whether this action can end the game in a loss for the bottom
readonly canFinishBottom: boolean
}
export interface PlayerState {
// This player's name
readonly name: string
// Amount of damage taken so far this battle
readonly damage: number
// Max damage that can be taken, 0 for endless mode
readonly damageMax: number
// The total damage taken across this battle
readonly damageTotal: number
// Number of stop dice required to end a turn with stops
readonly stopCount: number
// Number of fail dice required to fail a turn by accumulating too many fail dice
readonly failCount: number
// Incoming damage for this side is multiplied by 100% + 10% * damageBonuses
readonly damageBonuses: number
// Amount that Total Damage must reach to reduce this player's damage by 50% of its current value
// 0 means no recovery is allowed for this player
readonly nextRecoverAt: number
// Number of times damage was recovered so far this game (adds 1000 + 100 * timesRecovered to nextRecoverAt)
readonly timesRecovered: number
}
export interface GameState {
// The version of the serialization format this state was saved with.
readonly version: number
// The state of the top player
readonly top: PlayerState
// The state of the bottom player
readonly bottom: PlayerState
// The theme that defines this game for the players.
readonly theme: GameTheme
// The difficulty selected for this game.
readonly difficulty: Difficulty
// Null if the top is a computer.
readonly topHumanId: string | null
// Null if the bottom is a computer.
readonly bottomHumanId: string | null
// If true, the top is choosing the action this round.
readonly isTopRound: boolean
// The action chosen for this round, or null if the current player is choosing an action
readonly action: GameAction | null
// If true, the top is rolling this turn.
readonly isTopTurn: boolean
// The total for the previous turn, or 0 if the current player is taking the first turn
// If the current player fails when the turn total is 0, they take the penalty and pass the initiative without taking damage.
// This can still end the game if the current action is capable of ending the game for this player.
readonly lastTurnTotal: number
// The dice available for the current turn, or null if the current player has not rolled yet this turn.
readonly lastRoll: readonly DieResult[] | null
// The total value of the selected dice, or 0 if the selected dice cannot be scored.
readonly selectedDiceValue: number
// Whether the selected dice are sufficient to end the turn.
readonly selectedDiceEndTurn: boolean
// The number of dice that have come up failures so far in the current turn
readonly countedFails: number
// The total of the dice that have been scored so far this turn
readonly currentTurnTotal: number
}