Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
99e1571
[AI-Rework] Add starter code for move scoring functions (#356)
innerthunder Jan 26, 2025
a6fd730
Merge branch 'beta' into ai-rework
innerthunder Jan 26, 2025
dcb5294
Merge branch 'beta' into ai-rework
innerthunder Jan 31, 2025
8683c7a
Merge branch 'beta' into ai-rework
innerthunder Feb 4, 2025
ab30a8e
[AI-Rework] Rewrite enemy command selection (#403)
innerthunder Feb 6, 2025
d750b99
[AI-Rework] Add move-property-based scoring (#442)
innerthunder Feb 9, 2025
aaed8bf
Merge branch 'beta' into ai-rework
innerthunder Feb 9, 2025
62bcdc7
Prettier
innerthunder Feb 9, 2025
e3f3e56
Merge branch 'beta' into ai-rework
innerthunder Mar 5, 2025
7922965
Fix remaining conflicts from latest merge
innerthunder Mar 6, 2025
7c6dd9a
[AI-Rework] Add Move Attribute scoring parameters under `MoveAttrOpti…
innerthunder Mar 10, 2025
7155e44
Merge branch 'beta' into ai-rework
innerthunder Mar 13, 2025
28f65ee
Resolve remaining merge conflicts
innerthunder Mar 13, 2025
9246262
Merge branch 'beta' into ai-rework
innerthunder May 2, 2025
7f6b1bc
Resolve leftover conflicts from last merge
innerthunder May 2, 2025
6e2538e
Merge branch 'beta' into ai-rework
innerthunder May 5, 2025
19def79
Resolve remaining conflicts from last merge
innerthunder May 5, 2025
515eb12
[Addendum][AI-Rework][Test] Add matchers + Improve test utils (#1125)
innerthunder May 6, 2025
ffc11ff
[AI-Rework] Add effect scores for ability-changing effects (#834)
innerthunder May 9, 2025
56c5493
[AI-Rework] Add effect scores for arena-tag-based moves (#1158)
innerthunder May 14, 2025
899189d
Merge branch 'beta' into ai/beta-update-1
innerthunder May 15, 2025
700193e
Fix typecheck and linter issues
innerthunder May 15, 2025
dadd6c9
[AI-Rework] Rewrite `MoveCondition` + update Condition Score logic (#…
innerthunder May 21, 2025
7364400
Merge branch 'beta' into ai-rework
innerthunder May 23, 2025
006c3ce
Resolve remaining conflicts from last merge
innerthunder May 23, 2025
c1095d5
[AI-Rework] Remove `appliesScoreOnKO` option (#1222)
innerthunder May 27, 2025
30099ae
[AI-Rework] Add caching for invariant scoring calculations (#1258)
innerthunder Jun 21, 2025
1e06496
Merge branch 'beta' into ai-rework
innerthunder Jun 30, 2025
ede5db1
Run linter
innerthunder Jun 30, 2025
f95af98
[AI-Rework] Re-implement Instant Tera commands (#1283)
innerthunder Jul 17, 2025
6fafb86
Resolve merge issues
DayKev Jul 19, 2025
e5421d8
Remove unnecessary optional chaining
DayKev Jul 19, 2025
d7a7750
Apply review suggestions
innerthunder Jul 25, 2025
bbbb984
[AI-Rework] Add effect scores for moves with `BattlerTag` effects (#1…
innerthunder Jul 28, 2025
556a779
[AI-Rework] Add Effect Scores for Arena/Battler-tag-modifying effects…
innerthunder Aug 4, 2025
651c183
Merge branch 'beta' into ai-rework
innerthunder Aug 4, 2025
b2f357d
Merge branch 'ai-rework' of https://github.com/Despair-Games/poketern…
innerthunder Aug 4, 2025
0ea4e60
Resolve merge issues
innerthunder Aug 4, 2025
17b1c37
Fix flaky Rapid Spin tests
innerthunder Aug 4, 2025
b8fe6d0
ai-rework: Add Effect Scores for Status Effect inflicting moves (#1312)
innerthunder Aug 14, 2025
367886d
Simplify Wake Up Slap condition
innerthunder Aug 14, 2025
34e12f9
ai-rework: Add Effect Scores for misc. status-effect-related attribut…
innerthunder Aug 20, 2025
ebc2e1a
ai-rework: Add Effect Scores for healing effects (#1330)
innerthunder Aug 20, 2025
5c07d48
Merge branch 'beta' into ai-rework
innerthunder Aug 20, 2025
810d6c9
Fix linter issues
innerthunder Aug 20, 2025
783e2ee
Merge branch 'beta' into ai-rework
innerthunder Aug 21, 2025
b669649
Run linters
innerthunder Aug 21, 2025
b50943f
ai-rework: Rewrite Ally Target scoring logic (#1331)
innerthunder Aug 23, 2025
4814b46
ai-rework: Add scoring for `StatStageChangeAttr` + subclasses (#1337)
innerthunder Aug 28, 2025
9e94fca
Merge branch 'beta' into ai-rework
innerthunder Aug 28, 2025
cf0d2d4
Fix leftover conflict in `overrides-helper`
innerthunder Aug 28, 2025
42b9785
Reduce Focus Energy test flakiness
innerthunder Aug 28, 2025
d9069b8
ai-rework: Move ability- and move-revealing test utils to `FieldHelpe…
innerthunder Aug 29, 2025
5eafff2
Add AI paths to `create-test` script
innerthunder Aug 29, 2025
eba3d5f
ai-rework: Add Effect Scores for misc. stat-modifying attributes (#1340)
innerthunder Aug 31, 2025
14cdeb9
ai-rework: Rework multi-target move scoring logic (#1344)
innerthunder Aug 31, 2025
816c015
ai-rework: Add Effect Scores for self-damaging move effects (#1346)
innerthunder Sep 5, 2025
c35fef1
ai-rework: Add Effect Scores for Terrain- and Weather-changing effect…
innerthunder Sep 14, 2025
ad39b43
ai-rework: Update Effect Score calculation for charge moves (#1358)
innerthunder Sep 14, 2025
204dfd8
Merge branch 'beta' into ai-rework
innerthunder Sep 16, 2025
c80f075
Merge branch 'beta' into ai-rework
innerthunder Sep 16, 2025
5015ca4
Replace `isNil` references
innerthunder Sep 16, 2025
2bedbae
ai-rework: Add Effect Scores for type-modifying moves (#1362)
innerthunder Sep 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scripts/create-test/create-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const choices = [
{ label: "Mystery Encounter", dir: "mystery-encounter/encounters" },
{ label: "Utils", dir: "utils" },
{ label: "UI", dir: "ui" },
{ label: "AI (Move Effect Scores)", dir: "ai/move-effect-scores" },
{ label: "AI (Move Condition Scores)", dir: "ai/move-condition-scores" },
];

//#endregion
Expand Down
8 changes: 8 additions & 0 deletions src/@types/move-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { MoveId } from "#enums/move-id";

/**
* A function to filter moves based on their properties.
* @param moveId - The {@linkcode MoveId} of the move to evaluate
* @returns `true` if the filter applies to the given move
*/
export type MoveFilter = (moveId: MoveId) => boolean;
11 changes: 10 additions & 1 deletion src/@types/move-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,16 @@ export type DamageResult =
// TODO: Can these be combined into one? Should all of the params be optional in PAC?
export type PokemonAttackCondition = (user?: Pokemon, target?: Pokemon, move?: Move) => boolean;
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
export type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;

/**
* A function representing a {@linkcode Move | Move's} condition.
* @param user - The {@linkcode Pokemon} using the move
* @param target - The {@linkcode Pokemon} targeted by the move
* @param move - The {@linkcode Move} being used
* @param simulated - (Optional) If `true`, suppresses changes to game state
* @returns `true` if the condition is satisfied in the given battle state
*/
export type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move, simulated?: boolean) => boolean;

/**
* A function to check if a move with the given {@linkcode MoveId}
Expand Down
25 changes: 25 additions & 0 deletions src/@types/pokemon-score-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-enable @typescript-eslint/no-unused-vars */
// -- end tsdoc imports --

import type { MoveId } from "#enums/move-id";

/**
* Caches invariant data for the Enemy AI to reuse
* during its score calculations for turn action selection against
* a "target" {@linkcode Pokemon}. This is stored within a "source"
* Pokemon's {@linkcode PokemonTurnData}, and is thus reset at the start of each turn.
*/
export interface PokemonScoreData {
/**
* A score for grading the source Pokemon's matchup against
* the target Pokemon.
* @see {@linkcode EnemyPokemon.getMatchupScore}
*/
matchupScore?: number;
/**
* The expected values of the source Pokemon's Attack Scores
* against the target Pokemon for each of the source's moves.
* @see {@linkcode Pokemon.getExpectedAttackScore}
*/
expectedAttackScores: Map<MoveId, number>;
}
13 changes: 13 additions & 0 deletions src/@types/pokemon-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { SpeciesId } from "#enums/species-id";
import type { StatusEffect } from "#enums/status-effect";
import type { PokemonMove } from "#field/pokemon-move";
import type { AttackMoveResult, TurnMove } from "#types/move-types";
import type { PokemonScoreData } from "#types/pokemon-score-data";

/**
* The type that {@linkcode PokemonSpeciesForm} is converted to when an object containing it serializes it.
Expand Down Expand Up @@ -113,6 +114,11 @@ export interface PokemonTurnData {
switchedInThisTurn: boolean;
failedRunAway: boolean;
joinedRound: boolean;
/**
* Contains invariant scoring data for this Pokemon
* against all Pokemon on the field, mapped by their id.
*/
scoreData: Map<number, PokemonScoreData>;
}

/** Container for Pokemon-specific data that resets at the end of each wave. */
Expand All @@ -130,6 +136,8 @@ export interface PokemonWaveData {
* without applying them.
*/
abilitiesRevealed: AbilityId[];
/** The moves revealed from this Pokemon */
revealedMoves: Set<MoveId>;
}

/**
Expand Down Expand Up @@ -160,6 +168,11 @@ export interface Status {
* @defaultValue 0
*/
toxicTurnCount: number;
/**
* The number of turns the affected Pokemon has been sleeping.
* @defaultValue 0
*/
turnsAsleep: number;
/**
* The pokemon wakes up when this is `0` and the {@linkcode effect} is {@linkcode StatusEffect.SLEEP}.
* Ignored if the effect is not sleep.
Expand Down
106 changes: 106 additions & 0 deletions src/constants/ability-constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */
import type { Ability } from "#abilities/ability";
import type { SuppressWeatherEffectAbAttr } from "#abilities/suppress-weather-effect-ab-attr";
import type { WeatherType } from "#enums/weather-type";
/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */

import { AbilityId } from "#enums/ability-id";
Expand All @@ -16,3 +17,108 @@ export const IGNORING_ABILITIES = Object.freeze([AbilityId.MOLD_BREAKER, Ability
* @see {@linkcode SuppressWeatherEffectAbAttr}
*/
export const WEATHER_SUPPRESSING_ABILITIES = Object.freeze([AbilityId.CLOUD_NINE, AbilityId.AIR_LOCK]);

/** Abilities perceived by the Enemy AI to have high value */
export const HIGH_VALUE_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.WONDER_GUARD,
AbilityId.DESOLATE_LAND,
AbilityId.PRIMORDIAL_SEA,
AbilityId.HUGE_POWER,
AbilityId.PURE_POWER,
AbilityId.CONTRARY,
]);

/** Abilities perceived by the Enemy AI to have a detrimental effect on the source */
export const DETRIMENTAL_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.TRUANT,
AbilityId.WIMP_OUT,
AbilityId.EMERGENCY_EXIT,
AbilityId.DEFEATIST,
AbilityId.KLUTZ,
AbilityId.SLOW_START,
]);

/** Abilities that grant an immunity to Electric-type moves */
export const ELECTRIC_IMMUNE_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.VOLT_ABSORB,
AbilityId.LIGHTNING_ROD,
AbilityId.MOTOR_DRIVE,
]);

/** Abilities that grant a benefit when the source is under a non-volatile status effect */
export const ANY_STATUS_SYNERGY_ABILITIES = Object.freeze<AbilityId[]>([
AbilityId.GUTS,
AbilityId.QUICK_FEET,
AbilityId.MARVEL_SCALE,
AbilityId.MAGIC_GUARD,
]);

/** Abilities that grant a benefit when the source is burned */
export const BURN_SYNERGY_ABILITIES = Object.freeze<AbilityId[]>([
...ANY_STATUS_SYNERGY_ABILITIES,
AbilityId.FLARE_BOOST,
]);

/** Abilities that grant a benefit when the source is poisoned (or badly poisoned) */
export const POISON_SYNERGY_ABILITIES = Object.freeze<AbilityId[]>([
...ANY_STATUS_SYNERGY_ABILITIES,
AbilityId.TOXIC_BOOST,
AbilityId.POISON_HEAL,
]);

/** Abilities that grant a benefit when the source poisons another Pokemon */
export const POISONING_SYNERGY_ABILITIES = Object.freeze<AbilityId[]>([
AbilityId.MERCILESS,
AbilityId.POISON_PUPPETEER,
]);

/** Abilities that grant a benefit when receiving a negative stat stage change */
export const POST_STAT_STAGE_REDUCTION_ABILITIES = Object.freeze<AbilityId[]>([
AbilityId.DEFIANT,
AbilityId.COMPETITIVE,
]);

/** Abilities that grant the source immunity to recoil damage from its attacks */
export const RECOIL_DAMAGE_PREVENTION_ABILITIES = Object.freeze<AbilityId[]>([
AbilityId.ROCK_HEAD,
AbilityId.MAGIC_GUARD,
]);

/**
* Abilities that grant a benefit when {@link WeatherType.SUNNY | harsh sunlight}
* or {@link WeatherType.HARSH_SUN | extremely harsh sunlight} is in effect
*/
export const SUN_SYNERGY_ABILITIES = Object.freeze<Set<AbilityId>>(
new Set([
AbilityId.CHLOROPHYLL,
AbilityId.SOLAR_POWER,
AbilityId.FLOWER_GIFT,
AbilityId.LEAF_GUARD,
AbilityId.PROTOSYNTHESIS,
AbilityId.FORECAST,
]),
);

/**
* Abilities that grant a benefit when {@link WeatherType.RAIN | rain}
* or {@link WeatherType.HEAVY_RAIN | heavy rain} is in effect
*/
export const RAIN_SYNERGY_ABILITIES = Object.freeze<Set<AbilityId>>(
new Set([AbilityId.SWIFT_SWIM, AbilityId.RAIN_DISH, AbilityId.DRY_SKIN, AbilityId.HYDRATION, AbilityId.FORECAST]),
);

/**
* Abilities that grant a benefit when a {@link WeatherType.SANDSTORM | sandstorm}
* is in effect
*/
export const SAND_SYNERGY_ABILITIES = Object.freeze<Set<AbilityId>>(
new Set([AbilityId.SAND_FORCE, AbilityId.SAND_RUSH, AbilityId.SAND_VEIL, AbilityId.MAGIC_GUARD, AbilityId.OVERCOAT]),
);

/**
* Abilities that grant a benefit when {@link WeatherType.HAIL | hail}
* or {@link WeatherType.SNOW | snow} is in effect
*/
export const SNOW_SYNERGY_ABILITIES = Object.freeze<Set<AbilityId>>(
new Set([AbilityId.ICE_BODY, AbilityId.SNOW_CLOAK, AbilityId.SLUSH_RUSH, AbilityId.ICE_FACE]),
);
142 changes: 142 additions & 0 deletions src/constants/ai-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */
import type { EnemyPokemon } from "#field/enemy-pokemon";
import type { PlayerPokemon } from "#field/player-pokemon";
import type { Pokemon } from "#field/pokemon";
import type { MoveAttr } from "#moves/move-attr";
/* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */

import { AbilityApplyMode } from "#enums/ability-apply-mode";
import type { EffectiveStatOptions } from "#field/pokemon";

/** The {@link Pokemon.getAttackScore | Attack Score} granted to moves that KO an opponent. */
export const KO_ATTACK_SCORE = 4;

/**
* The damage (in terms of % maximum HP) a projected attack must deal
* to a target to reach the next scoring "tier". An attack projected to deal
* damage below this threshold will receive an Attack Score of either 0 or 1.
* On the other hand, attacks projected to deal damage above this threshold
* will receive an Attack Score of either 1 or 2.
* @see {@linkcode Pokemon.getAttackScore}
* @see {@linkcode Pokemon.getExpectedAttackScore}
*/
export const ATTACK_SCORE_HP_THRESHOLD = 40;

/**
* A relatively minor bonus to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* Used when a move with the attribute gains a slight advantage in
* a given battle state.
*/
export const MINOR_EFFECT_SCORE_BONUS = 1;

/**
* A relatively major bonus to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* Used when a move with the attribute gains a decisive advantage in
* a given battle state.
*/
export const MAJOR_EFFECT_SCORE_BONUS = 2;

/**
* A drastic bonus to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* This should be used very sparingly; it tows the line of Effect Scores'
* {@link SOFT_EFFECT_SCORE_LIMIT | soft limit} and may cause some moves
* to surpass the scores of moves that can KO if not used carefully.
*/
export const DRASTIC_EFFECT_SCORE_BONUS = 3;

/**
* A relatively minor penalty to a move's score. Used when a move has
* a potential drawback or has a chance of failing from an unresolvable condition
* in a given battle state.
*/
export const MINOR_EFFECT_SCORE_PENALTY = -MINOR_EFFECT_SCORE_BONUS;

/**
* A relatively major penalty to a move's score. Used when a move has
* a significant drawback or is likely to fail from an unresolvable condition
* in a given battle state.
*/
export const MAJOR_EFFECT_SCORE_PENALTY = -MAJOR_EFFECT_SCORE_BONUS;

/**
* A drastic penalty to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* The AI should avoid moves with this penalty outside of extreme circumstances.
*/
export const DRASTIC_EFFECT_SCORE_PENALTY = -DRASTIC_EFFECT_SCORE_BONUS;

/**
* A weakly enforced upper limit for move attributes' {@link MoveAttr.getEffectScore | Effect Scores}.
* Attributes generally shouldn't give enough of a bonus to supersede
* attacks that can KO opponents.
*/
export const SOFT_EFFECT_SCORE_LIMIT = KO_ATTACK_SCORE - 1;

/**
* The score penalty granted to moves that effectively do nothing, e.g.
* - Attacking into an opponent with a known immunity via typing, Ability, etc.
* - Using a move that is known to fail before selection.
* - Using Skill Swap on an enemy with a bad ability (e.g. Truant)
*/
export const BAD_MOVE_PENALTY = -5;

/**
* The score penalty granted by default when targeting an ally in a double battle.
* This should prevent the AI from deciding to attack their ally, for example.
* Certain move attributes may {@link MoveAttr.getAllyTargetScore | override this penalty}.
*/
export const ALLY_TARGET_PENALTY = -20;

/**
* The score penalty granted when targeting a Pokemon that is {@link https://bulbapedia.bulbagarden.net/wiki/Commander_(Ability) | Commanding}
* its ally. The AI should always expect this move to miss.
*/
export const COMMANDING_TARGET_PENALTY = -20;

/**
* A {@link Pokemon.getMatchupScore | Matchup Score} value indicating a
* Pokemon is strongly favored against its opponent
*/
export const STRONG_MATCHUP_SCORE_THRESHOLD = 4;

/**
* A {@link Pokemon.getMatchupScore | Matchup Score} value indicating a
* Pokemon is favored against its opponent
*/
export const FAVORABLE_MATCHUP_SCORE_THRESHOLD = 3;

/** The minimum accuracy a move can have before a Low Accuracy Penalty applies */
export const LOW_ACCURACY_PENALTY_THRESHOLD = 80;

/**
* The amount of stat stages an Enemy Pokemon can have before Defense- or
* Sp. Def-boosting moves (e.g. from Iron Defense) are significantly less
* incentivized by the AI
*/
export const DEFENSE_LOW_INCENTIVE_THRESHOLD = 2;

/**
* The amount of stat stages an Enemy Pokemon can have before Evasion-boosting
* moves (e.g. Double Team) are no longer incentivized by the AI
*/
export const EVASION_BOOST_STAGE_LIMIT = 2;

/**
* The amount of stat stages a Player Pokemon can have before Accuracy-reducing
* moves (e.g. Sand Attack) are no longer incentivized by the AI
*/
export const ACCURACY_REDUCTION_STAGE_LIMIT = -EVASION_BOOST_STAGE_LIMIT;

/**
* The {@linkcode EffectiveStatOptions} to be passed to {@linkcode Pokemon.getEffectiveStat}
* when evaluating the effective stat of an allied {@linkcode EnemyPokemon}
*/
export const ALLY_EFFECTIVE_STAT_OPTIONS: EffectiveStatOptions = { simulated: true } as const;

/**
* The {@linkcode EffectiveStatOptions} to be passed to {@linkcode Pokemon.getEffectiveStat}
* when evaluating the effective stat of an opposing {@linkcode PlayerPokemon}
*/
export const OPP_EFFECTIVE_STAT_OPTIONS: EffectiveStatOptions = {
abilityApplyMode: AbilityApplyMode.REVEALED,
simulated: true,
} as const;
25 changes: 25 additions & 0 deletions src/constants/arena-tag-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ export const WEAKEN_MOVE_SCREEN_ARENA_TAG_TYPES = Object.freeze<ArenaTagType[]>(
ArenaTagType.LIGHT_SCREEN,
]);

/**
* All {@linkcode ArenaTagType | ArenaTagTypes} that can be removed by
* {@link https://bulbapedia.bulbagarden.net/wiki/Defog_(move) | Defog}
* and equivalent effects from the target's side of the field
*/
export const DEFOG_REMOVABLE_ARENA_TAG_TYPES = Object.freeze<ArenaTagType[]>([
...WEAKEN_MOVE_SCREEN_ARENA_TAG_TYPES,
ArenaTagType.SAFEGUARD,
ArenaTagType.MIST,
]);

/** All {@linkcode ArenaTagType | ArenaTagTypes} that grant protection/invulnerability under certain conditions. */
export const CONDITIONAL_PROTECT_ARENA_TAG_TYPES = Object.freeze<ArenaTagType[]>([
ArenaTagType.QUICK_GUARD,
Expand Down Expand Up @@ -56,3 +67,17 @@ export const COURT_CHANGE_ARENA_TAG_TYPES = Object.freeze<ArenaTagType[]>([
ArenaTagType.G_MAX_CANNONADE,
ArenaTagType.G_MAX_VOLCALITH,
]);

/**
* All {@linkcode ArenaTagType | ArenaTagTypes} that can be swapped by {@linkcode MoveId.COURT_CHANGE}
* and are perceived by the Enemy AI to have a harmful effect.
*/
export const HARMFUL_COURT_CHANGE_ARENA_TAG_TYPES = Object.freeze<ArenaTagType[]>([
...ENTRY_HAZARD_ARENA_TAG_TYPES,
ArenaTagType.GRASS_WATER_PLEDGE,
ArenaTagType.FIRE_GRASS_PLEDGE,
ArenaTagType.G_MAX_VINE_LASH,
ArenaTagType.G_MAX_WILDFIRE,
ArenaTagType.G_MAX_CANNONADE,
ArenaTagType.G_MAX_VOLCALITH,
]);
Loading