Renamed arena traps to entry hazards fr fr

This commit is contained in:
Bertie690 2025-08-16 19:04:25 -04:00
parent db0c43e367
commit 08ee9e62aa
12 changed files with 55 additions and 52 deletions

View File

@ -2,7 +2,7 @@ import type { ArenaTagTypeMap } from "#data/arena-tag";
import type { ArenaTagType } from "#enums/arena-tag-type";
/** Subset of {@linkcode ArenaTagType}s that apply some negative effect to pokemon that switch in ({@link https://bulbapedia.bulbagarden.net/wiki/List_of_moves_that_cause_entry_hazards#List_of_traps | entry hazards} and Imprison. */
export type ArenaTrapTagType =
export type EntryHazardTagType =
| ArenaTagType.STICKY_WEB
| ArenaTagType.SPIKES
| ArenaTagType.TOXIC_SPIKES

View File

@ -6,7 +6,7 @@ import type { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-chang
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#data/arena-tag";
import type { EntryHazardTag, SuppressAbilitiesTag } from "#data/arena-tag";
import type { BattlerTag } from "#data/battler-tags";
import { GroundedTag } from "#data/battler-tags";
import { getBerryEffectFunc } from "#data/berry";
@ -1113,7 +1113,7 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
}
override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean {
const tag = globalScene.arena.getTag(this.arenaTagType) as ArenaTrapTag;
const tag = globalScene.arena.getTag(this.arenaTagType) as EntryHazardTag;
return (
this.condition(pokemon, attacker, move) &&
(!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers)

View File

@ -24,7 +24,7 @@ import type { Pokemon } from "#field/pokemon";
import type {
ArenaScreenTagType,
ArenaTagTypeData,
ArenaTrapTagType,
EntryHazardTagType,
SerializableArenaTagType,
} from "#types/arena-tags";
import type { Mutable } from "#types/type-helpers";
@ -725,12 +725,12 @@ export class IonDelugeTag extends ArenaTag {
}
/**
* Abstract class to implement [arena traps (AKA entry hazards)](https://bulbapedia.bulbagarden.net/wiki/List_of_moves_that_cause_entry_hazards).
* Abstract class to implement [entry hazards](https://bulbapedia.bulbagarden.net/wiki/List_of_moves_that_cause_entry_hazards).
* These persistent tags remain on-field across turns and apply effects to any {@linkcode Pokemon} switching in. \
* Uniquely, adding a tag multiple times will stack multiple "layers" of the effect, increasing its severity.
* Uniquely, adding a tag multiple times may stack multiple "layers" of the effect, increasing its severity.
*/
export abstract class ArenaTrapTag extends SerializableArenaTag {
abstract readonly tagType: ArenaTrapTagType;
export abstract class EntryHazardTag extends SerializableArenaTag {
public declare abstract readonly tagType: EntryHazardTagType;
/**
* The current number of layers this tag has.
* Starts at 1 and increases each time the trap is laid.
@ -837,7 +837,7 @@ export abstract class ArenaTrapTag extends SerializableArenaTag {
* Abstract class to implement damaging entry hazards.
* Currently used for {@linkcode SpikesTag} and {@linkcode StealthRockTag}.
*/
abstract class DamagingTrapTag extends ArenaTrapTag {
abstract class DamagingTrapTag extends EntryHazardTag {
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
// Check for magic guard immunity
const cancelled = new BooleanHolder(false);
@ -956,7 +956,7 @@ class StealthRockTag extends DamagingTrapTag {
* based on the current layer count. \
* Poison-type Pokémon will remove it entirely upon switch-in.
*/
class ToxicSpikesTag extends ArenaTrapTag {
class ToxicSpikesTag extends EntryHazardTag {
/**
* Whether the tag is currently in the process of being neutralized by a Poison-type.
* @defaultValue `false`
@ -1024,7 +1024,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Sticky_Web_(move) | Sticky Web}.
* Applies a single-layer trap that lowers the Speed of all grounded Pokémon switching in.
*/
class StickyWebTag extends ArenaTrapTag {
class StickyWebTag extends EntryHazardTag {
public readonly tagType = ArenaTagType.STICKY_WEB;
public override get maxLayers() {
return 1 as const;
@ -1087,7 +1087,7 @@ class StickyWebTag extends ArenaTrapTag {
* Imprison remains in effect as long as the source Pokemon is active and present on the field.
* Imprison will apply to any opposing Pokemon that switch onto the field as well.
*/
class ImprisonTag extends ArenaTrapTag {
class ImprisonTag extends EntryHazardTag {
public readonly tagType = ArenaTagType.IMPRISON;
public override get maxLayers() {
return 1 as const;

View File

@ -6,7 +6,7 @@ import { loggedInUser } from "#app/account";
import type { GameMode } from "#app/game-mode";
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import type { ArenaTrapTag } from "#data/arena-tag";
import type { EntryHazardTag } from "#data/arena-tag";
import { WeakenMoveTypeTag } from "#data/arena-tag";
import { MoveChargeAnim } from "#data/battle-anims";
import {
@ -6083,7 +6083,7 @@ export class AddArenaTrapTagAttr extends AddArenaTagAttr {
getCondition(): MoveConditionFunc {
return (user, target, move) => {
const side = (this.selfSideTarget !== user.isPlayer()) ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER;
const tag = globalScene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
const tag = globalScene.arena.getTagOnSide(this.tagType, side) as EntryHazardTag;
if (!tag) {
return true;
}
@ -6107,7 +6107,7 @@ export class AddArenaTrapTagHitAttr extends AddArenaTagAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
const tag = globalScene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
const tag = globalScene.arena.getTagOnSide(this.tagType, side) as EntryHazardTag;
if ((moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) {
globalScene.arena.addTag(this.tagType, 0, move.id, user.id, side);
if (!tag) {

View File

@ -8,7 +8,7 @@ import Overrides from "#app/overrides";
import type { BiomeTierTrainerPools, PokemonPools } from "#balance/biomes";
import { BiomePoolTier, biomePokemonPools, biomeTrainerPools } from "#balance/biomes";
import type { ArenaTag } from "#data/arena-tag";
import { ArenaTrapTag, getArenaTag } from "#data/arena-tag";
import { EntryHazardTag, getArenaTag } from "#data/arena-tag";
import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#data/form-change-triggers";
import type { PokemonSpecies } from "#data/pokemon-species";
import { PositionalTagManager } from "#data/positional-tags/positional-tag-manager";
@ -709,8 +709,8 @@ export class Arena {
if (existingTag) {
existingTag.onOverlap(this, globalScene.getPokemonById(sourceId));
if (existingTag instanceof ArenaTrapTag) {
const { tagType, side, turnCount, layers, maxLayers } = existingTag as ArenaTrapTag;
if (existingTag instanceof EntryHazardTag) {
const { tagType, side, turnCount, layers, maxLayers } = existingTag as EntryHazardTag;
this.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers));
}
@ -723,7 +723,7 @@ export class Arena {
newTag.onAdd(this, quiet);
this.tags.push(newTag);
const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {};
const { layers = 0, maxLayers = 0 } = newTag instanceof EntryHazardTag ? newTag : {};
this.eventTarget.dispatchEvent(
new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers),

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { pokemonPrevolutions } from "#balance/pokemon-evolutions";
import { signatureSpecies } from "#balance/signature-species";
import { ArenaTrapTag } from "#data/arena-tag";
import { EntryHazardTag } from "#data/arena-tag";
import type { PokemonSpecies } from "#data/pokemon-species";
import { ArenaTagSide } from "#enums/arena-tag-side";
import { PartyMemberStrength } from "#enums/party-member-strength";
@ -584,8 +584,8 @@ export class Trainer extends Phaser.GameObjects.Container {
score /= playerField.length;
if (forSwitch && !p.isOnField()) {
globalScene.arena
.findTagsOnSide(t => t instanceof ArenaTrapTag, ArenaTagSide.ENEMY)
.map(t => (score *= (t as ArenaTrapTag).getMatchupScoreMultiplier(p)));
.findTagsOnSide(t => t instanceof EntryHazardTag, ArenaTagSide.ENEMY)
.map(t => (score *= (t as EntryHazardTag).getMatchupScoreMultiplier(p)));
}
}

View File

@ -1,6 +1,6 @@
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
import { globalScene } from "#app/global-scene";
import { ArenaTrapTag } from "#data/arena-tag";
import { EntryHazardTag } from "#data/arena-tag";
import { MysteryEncounterPostSummonTag } from "#data/battler-tags";
import { BattlerTagType } from "#enums/battler-tag-type";
import { StatusEffect } from "#enums/status-effect";
@ -16,7 +16,7 @@ export class PostSummonPhase extends PokemonPhase {
if (pokemon.status?.effect === StatusEffect.TOXIC) {
pokemon.status.toxicTurnCount = 0;
}
globalScene.arena.applyTags(ArenaTrapTag, false, pokemon);
globalScene.arena.applyTags(EntryHazardTag, false, pokemon);
// If this is mystery encounter and has post summon phase tag, apply post summon effects
if (

View File

@ -10,7 +10,7 @@ import { Tutorial } from "#app/tutorial";
import { speciesEggMoves } from "#balance/egg-moves";
import { pokemonPrevolutions } from "#balance/pokemon-evolutions";
import { speciesStarterCosts } from "#balance/starters";
import { ArenaTrapTag } from "#data/arena-tag";
import { EntryHazardTag } from "#data/arena-tag";
import { allMoves, allSpecies } from "#data/data-lists";
import type { Egg } from "#data/egg";
import { pokemonFormChanges } from "#data/pokemon-forms";
@ -1135,8 +1135,8 @@ export class GameData {
globalScene.arena.tags = sessionData.arena.tags;
if (globalScene.arena.tags) {
for (const tag of globalScene.arena.tags) {
if (tag instanceof ArenaTrapTag) {
const { tagType, side, turnCount, layers, maxLayers } = tag as ArenaTrapTag;
if (tag instanceof EntryHazardTag) {
const { tagType, side, turnCount, layers, maxLayers } = tag as EntryHazardTag;
globalScene.arena.eventTarget.dispatchEvent(
new TagAddedEvent(tagType, side, turnCount, layers, maxLayers),
);

View File

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene";
import { ArenaTrapTag } from "#data/arena-tag";
import { EntryHazardTag } from "#data/arena-tag";
import { TerrainType } from "#data/terrain";
import { ArenaTagSide } from "#enums/arena-tag-side";
import { ArenaTagType } from "#enums/arena-tag-type";
@ -287,7 +287,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
switch (arenaEffectChangedEvent.constructor) {
case TagAddedEvent: {
const tagAddedEvent = arenaEffectChangedEvent as TagAddedEvent;
const isArenaTrapTag = globalScene.arena.getTag(tagAddedEvent.arenaTagType) instanceof ArenaTrapTag;
const isArenaTrapTag = globalScene.arena.getTag(tagAddedEvent.arenaTagType) instanceof EntryHazardTag;
let arenaEffectType: ArenaEffectType;
if (tagAddedEvent.arenaTagSide === ArenaTagSide.BOTH) {

View File

@ -1,4 +1,4 @@
import { ArenaTrapTag } from "#data/arena-tag";
import { EntryHazardTag } from "#data/arena-tag";
import { allMoves } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { ArenaTagSide } from "#enums/arena-tag-side";
@ -50,12 +50,12 @@ describe("Moves - Ceaseless Edge", () => {
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagBefore instanceof EntryHazardTag).toBeFalsy();
await game.phaseInterceptor.to(TurnEndPhase);
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagAfter instanceof EntryHazardTag).toBeTruthy();
expect(tagAfter.layers).toBe(1);
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
});
@ -72,12 +72,12 @@ describe("Moves - Ceaseless Edge", () => {
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagBefore instanceof EntryHazardTag).toBeFalsy();
await game.phaseInterceptor.to(TurnEndPhase);
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagAfter instanceof EntryHazardTag).toBeTruthy();
expect(tagAfter.layers).toBe(2);
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
});
@ -90,12 +90,12 @@ describe("Moves - Ceaseless Edge", () => {
game.move.select(MoveId.CEASELESS_EDGE);
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagBefore instanceof EntryHazardTag).toBeFalsy();
await game.toNextTurn();
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagAfter instanceof EntryHazardTag).toBeTruthy();
expect(tagAfter.layers).toBe(2);
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;

View File

@ -1,4 +1,4 @@
import type { ArenaTrapTag } from "#data/arena-tag";
import type { EntryHazardTag } from "#data/arena-tag";
import { allMoves } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id";
import { ArenaTagSide } from "#enums/arena-tag-side";
@ -195,7 +195,7 @@ describe("Moves - Destiny Bond", () => {
expect(playerPokemon.isFainted()).toBe(true);
// Ceaseless Edge spikes effect should still activate
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as EntryHazardTag;
expect(tagAfter.tagType).toBe(ArenaTagType.SPIKES);
expect(tagAfter.layers).toBe(1);
});
@ -220,7 +220,10 @@ describe("Moves - Destiny Bond", () => {
expect(playerPokemon1?.isFainted()).toBe(true);
// Pledge secondary effect should still activate
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, ArenaTagSide.ENEMY) as ArenaTrapTag;
const tagAfter = game.scene.arena.getTagOnSide(
ArenaTagType.GRASS_WATER_PLEDGE,
ArenaTagSide.ENEMY,
) as EntryHazardTag;
expect(tagAfter.tagType).toBe(ArenaTagType.GRASS_WATER_PLEDGE);
});

View File

@ -1,5 +1,4 @@
import { getPokemonNameWithAffix } from "#app/messages";
import { ArenaTrapTag } from "#data/arena-tag";
import { allMoves } from "#data/data-lists";
import type { TypeDamageMultiplier } from "#data/type";
import { AbilityId } from "#enums/ability-id";
@ -14,7 +13,7 @@ import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { GameManager } from "#test/test-utils/game-manager";
import type { ArenaTrapTagType } from "#types/arena-tags";
import type { EntryHazardTagType } from "#types/arena-tags";
import i18next from "i18next";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -46,7 +45,7 @@ describe("Moves - Entry Hazards", () => {
.battleType(BattleType.TRAINER);
});
describe.each<{ name: string; move: MoveId; tagType: ArenaTrapTagType }>([
describe.each<{ name: string; move: MoveId; tagType: EntryHazardTagType }>([
{ name: "Spikes", move: MoveId.SPIKES, tagType: ArenaTagType.SPIKES },
{
name: "Toxic Spikes",
@ -79,18 +78,19 @@ describe("Moves - Entry Hazards", () => {
// TODO: re-enable after re-fixing hazards moves
it.todo("should work when all targets fainted", async () => {
game.override.enemySpecies(SpeciesId.DIGLETT).battleStyle("double").startingLevel(1000);
game.override.battleStyle("double");
await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.SHUCKLE]);
const [enemy1, enemy2] = game.scene.getEnemyField();
game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER);
game.move.use(MoveId.SPIKES, BattlerIndex.PLAYER_2);
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
game.move.use(move, BattlerIndex.PLAYER_2);
await game.doKillOpponents();
await game.toEndOfTurn();
expect(enemy1.isFainted()).toBe(true);
expect(enemy2.isFainted()).toBe(true);
expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeDefined();
expect(game).toHaveArenaTag(tagType, ArenaTagSide.ENEMY);
});
const maxLayers = tagType === ArenaTagType.SPIKES ? 3 : tagType === ArenaTagType.TOXIC_SPIKES ? 2 : 1;