mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-08 00:19:29 +02:00
Compare commits
6 Commits
e97cfb3d30
...
3d5ff4cd3f
Author | SHA1 | Date | |
---|---|---|---|
|
3d5ff4cd3f | ||
|
bb86bdd2a6 | ||
|
1633df75c4 | ||
|
dc849bcd08 | ||
|
2b72077af4 | ||
|
349c1d052f |
@ -15,3 +15,10 @@ export interface AccountRegisterRequest {
|
|||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AccountChangePwRequest {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
export interface AccountChangePwResponse {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
17
src/@types/typed-event-target.ts
Normal file
17
src/@types/typed-event-target.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Interface restricting the events emitted by an {@linkcode EventTarget} to a certain kind of {@linkcode Event}.
|
||||||
|
* @typeParam T - The type to restrict the interface's access; must extend from {@linkcode Event}
|
||||||
|
*/
|
||||||
|
export interface TypedEventTarget<T extends Event = never> extends EventTarget {
|
||||||
|
dispatchEvent(event: T): boolean;
|
||||||
|
addEventListener(
|
||||||
|
event: T["type"],
|
||||||
|
callback: EventListenerOrEventListenerObject | null,
|
||||||
|
options?: AddEventListenerOptions | boolean,
|
||||||
|
): void;
|
||||||
|
removeEventListener(
|
||||||
|
type: T["type"],
|
||||||
|
callback: EventListenerOrEventListenerObject | null,
|
||||||
|
options?: EventListenerOptions | boolean,
|
||||||
|
): void;
|
||||||
|
}
|
@ -19,6 +19,7 @@ import { MoveTarget } from "#enums/move-target";
|
|||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { ArenaTagAddedEvent } from "#events/arena";
|
||||||
import type { Arena } from "#field/arena";
|
import type { Arena } from "#field/arena";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import type {
|
import type {
|
||||||
@ -729,7 +730,9 @@ export class IonDelugeTag extends ArenaTag {
|
|||||||
*/
|
*/
|
||||||
export abstract class ArenaTrapTag extends SerializableArenaTag {
|
export abstract class ArenaTrapTag extends SerializableArenaTag {
|
||||||
abstract readonly tagType: ArenaTrapTagType;
|
abstract readonly tagType: ArenaTrapTagType;
|
||||||
|
/** The tag's current number of layers. */
|
||||||
public layers: number;
|
public layers: number;
|
||||||
|
/** The maximum number of layers this tag can have. */
|
||||||
public maxLayers: number;
|
public maxLayers: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -749,11 +752,13 @@ export abstract class ArenaTrapTag extends SerializableArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onOverlap(arena: Arena, _source: Pokemon | null): void {
|
onOverlap(arena: Arena, _source: Pokemon | null): void {
|
||||||
if (this.layers < this.maxLayers) {
|
if (this.layers === this.maxLayers) {
|
||||||
this.layers++;
|
return;
|
||||||
|
|
||||||
this.onAdd(arena);
|
|
||||||
}
|
}
|
||||||
|
// Add an extra layer of the current hazard, then
|
||||||
|
this.layers++;
|
||||||
|
this.onAdd(arena);
|
||||||
|
arena.eventTarget.dispatchEvent(new ArenaTagAddedEvent(this.tagType, this.side, 0, [this.layers, this.maxLayers]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -771,9 +776,13 @@ export abstract class ArenaTrapTag extends SerializableArenaTag {
|
|||||||
return this.activateTrap(pokemon, simulated);
|
return this.activateTrap(pokemon, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
activateTrap(_pokemon: Pokemon, _simulated: boolean): boolean {
|
/**
|
||||||
return false;
|
* Trigger this trap's effects on any Pokemon switching into battle.
|
||||||
}
|
* @param _pokemon - The {@linkcode Pokemon} entering the field
|
||||||
|
* @param _simulated - Whether the switch is simulated
|
||||||
|
* @returns `true` if the effect succeeded
|
||||||
|
*/
|
||||||
|
abstract activateTrap(_pokemon: Pokemon, _simulated: boolean): boolean;
|
||||||
|
|
||||||
getMatchupScoreMultiplier(pokemon: Pokemon): number {
|
getMatchupScoreMultiplier(pokemon: Pokemon): number {
|
||||||
return pokemon.isGrounded()
|
return pokemon.isGrounded()
|
||||||
|
@ -1641,10 +1641,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [],
|
[BiomePoolTier.BOSS]: []
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.PLAINS]: {
|
[BiomeId.PLAINS]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.TWINS ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.TWINS ],
|
||||||
@ -1652,10 +1649,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.CILAN, TrainerType.CHILI, TrainerType.CRESS, TrainerType.CHEREN ],
|
[BiomePoolTier.BOSS]: [ TrainerType.CILAN, TrainerType.CHILI, TrainerType.CRESS, TrainerType.CHEREN ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.GRASS]: {
|
[BiomeId.GRASS]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.SCHOOL_KID ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.SCHOOL_KID ],
|
||||||
@ -1663,10 +1657,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.ERIKA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.ERIKA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.TALL_GRASS]: {
|
[BiomeId.TALL_GRASS]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [],
|
||||||
@ -1674,10 +1665,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.GARDENIA, TrainerType.VIOLA, TrainerType.BRASSIUS ],
|
[BiomePoolTier.BOSS]: [ TrainerType.GARDENIA, TrainerType.VIOLA, TrainerType.BRASSIUS ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.METROPOLIS]: {
|
[BiomeId.METROPOLIS]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.CLERK, TrainerType.CYCLIST, TrainerType.OFFICER, TrainerType.WAITER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.CLERK, TrainerType.CYCLIST, TrainerType.OFFICER, TrainerType.WAITER ],
|
||||||
@ -1685,10 +1673,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.ARTIST, TrainerType.RICH_KID ],
|
[BiomePoolTier.RARE]: [ TrainerType.ARTIST, TrainerType.RICH_KID ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.WHITNEY, TrainerType.NORMAN, TrainerType.IONO, TrainerType.LARRY ],
|
[BiomePoolTier.BOSS]: [ TrainerType.WHITNEY, TrainerType.NORMAN, TrainerType.IONO, TrainerType.LARRY ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.FOREST]: {
|
[BiomeId.FOREST]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.RANGER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.RANGER ],
|
||||||
@ -1696,10 +1681,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.BUGSY, TrainerType.BURGH, TrainerType.KATY ],
|
[BiomePoolTier.BOSS]: [ TrainerType.BUGSY, TrainerType.BURGH, TrainerType.KATY ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SEA]: {
|
[BiomeId.SEA]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.SAILOR, TrainerType.SWIMMER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.SAILOR, TrainerType.SWIMMER ],
|
||||||
@ -1707,10 +1689,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.MARLON ],
|
[BiomePoolTier.BOSS]: [ TrainerType.MARLON ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SWAMP]: {
|
[BiomeId.SWAMP]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.PARASOL_LADY ],
|
[BiomePoolTier.COMMON]: [ TrainerType.PARASOL_LADY ],
|
||||||
@ -1718,10 +1697,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.JANINE, TrainerType.ROXIE ],
|
[BiomePoolTier.BOSS]: [ TrainerType.JANINE, TrainerType.ROXIE ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.BEACH]: {
|
[BiomeId.BEACH]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.FISHERMAN, TrainerType.SAILOR ],
|
[BiomePoolTier.COMMON]: [ TrainerType.FISHERMAN, TrainerType.SAILOR ],
|
||||||
@ -1729,10 +1705,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.MISTY, TrainerType.KOFU ],
|
[BiomePoolTier.BOSS]: [ TrainerType.MISTY, TrainerType.KOFU ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.LAKE]: {
|
[BiomeId.LAKE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.FISHERMAN, TrainerType.PARASOL_LADY ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.FISHERMAN, TrainerType.PARASOL_LADY ],
|
||||||
@ -1740,21 +1713,15 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.CRASHER_WAKE ],
|
[BiomePoolTier.BOSS]: [ TrainerType.CRASHER_WAKE ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SEABED]: {
|
[BiomeId.SEABED]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [ TrainerType.SWIMMER ],
|
||||||
[BiomePoolTier.UNCOMMON]: [],
|
[BiomePoolTier.UNCOMMON]: [],
|
||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.JUAN ],
|
[BiomePoolTier.BOSS]: [ TrainerType.JUAN ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.MOUNTAIN]: {
|
[BiomeId.MOUNTAIN]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.BLACK_BELT, TrainerType.HIKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.BLACK_BELT, TrainerType.HIKER ],
|
||||||
@ -1762,10 +1729,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.FALKNER, TrainerType.WINONA, TrainerType.SKYLA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.FALKNER, TrainerType.WINONA, TrainerType.SKYLA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.BADLANDS]: {
|
[BiomeId.BADLANDS]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ],
|
||||||
@ -1773,10 +1737,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.CLAY, TrainerType.GRANT ],
|
[BiomePoolTier.BOSS]: [ TrainerType.CLAY, TrainerType.GRANT ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.CAVE]: {
|
[BiomeId.CAVE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ],
|
||||||
@ -1784,10 +1745,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.BROCK, TrainerType.ROXANNE, TrainerType.ROARK ],
|
[BiomePoolTier.BOSS]: [ TrainerType.BROCK, TrainerType.ROXANNE, TrainerType.ROARK ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.DESERT]: {
|
[BiomeId.DESERT]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.SCIENTIST ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.SCIENTIST ],
|
||||||
@ -1795,10 +1753,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.GORDIE ],
|
[BiomePoolTier.BOSS]: [ TrainerType.GORDIE ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.ICE_CAVE]: {
|
[BiomeId.ICE_CAVE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ],
|
||||||
@ -1806,10 +1761,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.PRYCE, TrainerType.BRYCEN, TrainerType.WULFRIC, TrainerType.GRUSHA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.PRYCE, TrainerType.BRYCEN, TrainerType.WULFRIC, TrainerType.GRUSHA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.MEADOW]: {
|
[BiomeId.MEADOW]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.MUSICIAN, TrainerType.PARASOL_LADY ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.MUSICIAN, TrainerType.PARASOL_LADY ],
|
||||||
@ -1817,10 +1769,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.LENORA, TrainerType.MILO ],
|
[BiomePoolTier.BOSS]: [ TrainerType.LENORA, TrainerType.MILO ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.POWER_PLANT]: {
|
[BiomeId.POWER_PLANT]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.GUITARIST, TrainerType.WORKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.GUITARIST, TrainerType.WORKER ],
|
||||||
@ -1828,10 +1777,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.VOLKNER, TrainerType.ELESA, TrainerType.CLEMONT ],
|
[BiomePoolTier.BOSS]: [ TrainerType.VOLKNER, TrainerType.ELESA, TrainerType.CLEMONT ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.VOLCANO]: {
|
[BiomeId.VOLCANO]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.FIREBREATHER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.FIREBREATHER ],
|
||||||
@ -1839,10 +1785,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.BLAINE, TrainerType.FLANNERY, TrainerType.KABU ],
|
[BiomePoolTier.BOSS]: [ TrainerType.BLAINE, TrainerType.FLANNERY, TrainerType.KABU ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.GRAVEYARD]: {
|
[BiomeId.GRAVEYARD]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC ],
|
[BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC ],
|
||||||
@ -1850,10 +1793,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.MORTY, TrainerType.ALLISTER, TrainerType.RYME ],
|
[BiomePoolTier.BOSS]: [ TrainerType.MORTY, TrainerType.ALLISTER, TrainerType.RYME ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.DOJO]: {
|
[BiomeId.DOJO]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BLACK_BELT ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BLACK_BELT ],
|
||||||
@ -1861,10 +1801,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.BRAWLY, TrainerType.MAYLENE, TrainerType.KORRINA, TrainerType.BEA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.BRAWLY, TrainerType.MAYLENE, TrainerType.KORRINA, TrainerType.BEA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.FACTORY]: {
|
[BiomeId.FACTORY]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.WORKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.WORKER ],
|
||||||
@ -1872,10 +1809,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.JASMINE, TrainerType.BYRON ],
|
[BiomePoolTier.BOSS]: [ TrainerType.JASMINE, TrainerType.BYRON ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.RUINS]: {
|
[BiomeId.RUINS]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC, TrainerType.SCIENTIST ],
|
[BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC, TrainerType.SCIENTIST ],
|
||||||
@ -1883,10 +1817,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.SABRINA, TrainerType.TATE, TrainerType.LIZA, TrainerType.TULIP ],
|
[BiomePoolTier.BOSS]: [ TrainerType.SABRINA, TrainerType.TATE, TrainerType.LIZA, TrainerType.TULIP ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.WASTELAND]: {
|
[BiomeId.WASTELAND]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.VETERAN ],
|
[BiomePoolTier.COMMON]: [ TrainerType.VETERAN ],
|
||||||
@ -1894,10 +1825,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.CLAIR, TrainerType.DRAYDEN, TrainerType.RAIHAN ],
|
[BiomePoolTier.BOSS]: [ TrainerType.CLAIR, TrainerType.DRAYDEN, TrainerType.RAIHAN ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.ABYSS]: {
|
[BiomeId.ABYSS]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [],
|
||||||
@ -1905,10 +1833,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.MARNIE ],
|
[BiomePoolTier.BOSS]: [ TrainerType.MARNIE ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SPACE]: {
|
[BiomeId.SPACE]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [],
|
||||||
@ -1916,10 +1841,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.OLYMPIA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.OLYMPIA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.CONSTRUCTION_SITE]: {
|
[BiomeId.CONSTRUCTION_SITE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.OFFICER, TrainerType.WORKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.OFFICER, TrainerType.WORKER ],
|
||||||
@ -1927,10 +1849,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.LT_SURGE, TrainerType.CHUCK, TrainerType.WATTSON ],
|
[BiomePoolTier.BOSS]: [ TrainerType.LT_SURGE, TrainerType.CHUCK, TrainerType.WATTSON ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.JUNGLE]: {
|
[BiomeId.JUNGLE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.RANGER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.RANGER ],
|
||||||
@ -1938,10 +1857,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.RAMOS ],
|
[BiomePoolTier.BOSS]: [ TrainerType.RAMOS ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.FAIRY_CAVE]: {
|
[BiomeId.FAIRY_CAVE]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY ],
|
||||||
@ -1949,10 +1865,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.VALERIE, TrainerType.OPAL, TrainerType.BEDE ],
|
[BiomePoolTier.BOSS]: [ TrainerType.VALERIE, TrainerType.OPAL, TrainerType.BEDE ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.TEMPLE]: {
|
[BiomeId.TEMPLE]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [],
|
||||||
@ -1960,10 +1873,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.FANTINA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.FANTINA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SLUM]: {
|
[BiomeId.SLUM]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.BIKER, TrainerType.OFFICER, TrainerType.ROUGHNECK ],
|
[BiomePoolTier.COMMON]: [ TrainerType.BIKER, TrainerType.OFFICER, TrainerType.ROUGHNECK ],
|
||||||
@ -1971,10 +1881,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.PIERS ],
|
[BiomePoolTier.BOSS]: [ TrainerType.PIERS ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.SNOWY_FOREST]: {
|
[BiomeId.SNOWY_FOREST]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ],
|
[BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ],
|
||||||
@ -1982,10 +1889,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.CANDICE, TrainerType.MELONY ],
|
[BiomePoolTier.BOSS]: [ TrainerType.CANDICE, TrainerType.MELONY ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.ISLAND]: {
|
[BiomeId.ISLAND]: {
|
||||||
[BiomePoolTier.COMMON]: [ TrainerType.RICH_KID ],
|
[BiomePoolTier.COMMON]: [ TrainerType.RICH_KID ],
|
||||||
@ -1993,21 +1897,15 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.NESSA ],
|
[BiomePoolTier.BOSS]: [ TrainerType.NESSA ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.LABORATORY]: {
|
[BiomeId.LABORATORY]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [ TrainerType.SCIENTIST ],
|
||||||
[BiomePoolTier.UNCOMMON]: [],
|
[BiomePoolTier.UNCOMMON]: [],
|
||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [ TrainerType.GIOVANNI ],
|
[BiomePoolTier.BOSS]: [ TrainerType.GIOVANNI ]
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
},
|
},
|
||||||
[BiomeId.END]: {
|
[BiomeId.END]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [],
|
||||||
@ -2015,13 +1913,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
[BiomePoolTier.ULTRA_RARE]: [],
|
[BiomePoolTier.ULTRA_RARE]: [],
|
||||||
[BiomePoolTier.BOSS]: [],
|
[BiomePoolTier.BOSS]: []
|
||||||
[BiomePoolTier.BOSS_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: [],
|
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function initBiomes() {
|
export function initBiomes() {
|
||||||
const pokemonBiomes = [
|
const pokemonBiomes = [
|
||||||
[ SpeciesId.BULBASAUR, PokemonType.GRASS, PokemonType.POISON, [
|
[ SpeciesId.BULBASAUR, PokemonType.GRASS, PokemonType.POISON, [
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
import { DelayedAttackTag, type PositionalTag, WishTag } from "#data/positional-tags/positional-tag";
|
import { DelayedAttackTag, type PositionalTag, WishTag } from "#data/positional-tags/positional-tag";
|
||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
|
import { PositionalTagAddedEvent } from "#events/arena";
|
||||||
import type { ObjectValues } from "#types/type-helpers";
|
import type { ObjectValues } from "#types/type-helpers";
|
||||||
import type { Constructor } from "#utils/common";
|
import type { Constructor } from "#utils/common";
|
||||||
|
|
||||||
@ -23,17 +25,15 @@ export function loadPositionalTag<T extends PositionalTagType>({
|
|||||||
* This function does not perform any checking if the added tag is valid.
|
* This function does not perform any checking if the added tag is valid.
|
||||||
*/
|
*/
|
||||||
export function loadPositionalTag(tag: SerializedPositionalTag): PositionalTag;
|
export function loadPositionalTag(tag: SerializedPositionalTag): PositionalTag;
|
||||||
export function loadPositionalTag<T extends PositionalTagType>({
|
export function loadPositionalTag(tag: SerializedPositionalTag): PositionalTag {
|
||||||
tagType,
|
// Update the global arena flyout
|
||||||
...rest
|
globalScene.arena.eventTarget.dispatchEvent(new PositionalTagAddedEvent(tag));
|
||||||
}: serializedPosTagMap[T]): posTagInstanceMap[T] {
|
|
||||||
// Note: We need 2 type assertions here:
|
// Create the new tag
|
||||||
// 1 because TS doesn't narrow the type of TagClass correctly based on `T`.
|
// TODO: review how many type assertions we need here
|
||||||
// It converts it into `new (DelayedAttackTag | WishTag) => DelayedAttackTag & WishTag`
|
const { tagType, ...rest } = tag;
|
||||||
const tagClass = posTagConstructorMap[tagType] as new (args: posTagParamMap[T]) => posTagInstanceMap[T];
|
const tagClass = posTagConstructorMap[tagType];
|
||||||
// 2 because TS doesn't narrow the type of `rest` correctly
|
return new tagClass(rest);
|
||||||
// (from `Omit<serializedPosTagParamMap[T], "tagType"> into `posTagParamMap[T]`)
|
|
||||||
return new tagClass(rest as unknown as posTagParamMap[T]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Const object mapping tag types to their constructors. */
|
/** Const object mapping tag types to their constructors. */
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
import { loadPositionalTag } from "#data/positional-tags/load-positional-tag";
|
import { loadPositionalTag } from "#data/positional-tags/load-positional-tag";
|
||||||
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
|
import { PositionalTagRemovedEvent } from "#events/arena";
|
||||||
|
|
||||||
/** A manager for the {@linkcode PositionalTag}s in the arena. */
|
/** A manager for the {@linkcode PositionalTag}s in the arena. */
|
||||||
export class PositionalTagManager {
|
export class PositionalTagManager {
|
||||||
@ -49,7 +51,16 @@ export class PositionalTagManager {
|
|||||||
if (tag.shouldTrigger()) {
|
if (tag.shouldTrigger()) {
|
||||||
tag.trigger();
|
tag.trigger();
|
||||||
}
|
}
|
||||||
|
this.emitRemove(tag);
|
||||||
}
|
}
|
||||||
this.tags = leftoverTags;
|
this.tags = leftoverTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a {@linkcode PositionalTagRemovedEvent} whenever a tag is removed from the field.
|
||||||
|
* @param tag - The {@linkcode PositionalTag} being removed
|
||||||
|
*/
|
||||||
|
private emitRemove(tag: PositionalTag): void {
|
||||||
|
globalScene.arena.eventTarget.dispatchEvent(new PositionalTagRemovedEvent(tag.tagType, tag.targetIndex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,18 @@
|
|||||||
|
import type { ArenaTag } from "#data/arena-tag";
|
||||||
|
import type { ArenaFlyout } from "#ui/arena-flyout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum used to represent a given side of the field for the purposes of {@linkcode ArenaTag}s and
|
||||||
|
* the current {@linkcode ArenaFlyout}.
|
||||||
|
*/
|
||||||
export enum ArenaTagSide {
|
export enum ArenaTagSide {
|
||||||
|
/**
|
||||||
|
* The effect applies to both sides of the field (player & enemy).
|
||||||
|
* Also used for the purposes of displaying weather and other "field-based" effects in the flyout.
|
||||||
|
*/
|
||||||
BOTH,
|
BOTH,
|
||||||
|
/** The effect applies exclusively to the player's side of the field. */
|
||||||
PLAYER,
|
PLAYER,
|
||||||
|
/** The effect applies exclusively to the opposing side of the field. */
|
||||||
ENEMY
|
ENEMY
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,34 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
|
|
||||||
export enum FieldPosition {
|
export enum FieldPosition {
|
||||||
CENTER,
|
CENTER,
|
||||||
LEFT,
|
LEFT,
|
||||||
RIGHT
|
RIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a {@linkcode BattlerIndex} into a field position.
|
||||||
|
* @param index - The {@linkcode BattlerIndex} to convert
|
||||||
|
* @returns The resultant field position.
|
||||||
|
*/
|
||||||
|
export function battlerIndexToFieldPosition(index: BattlerIndex): FieldPosition {
|
||||||
|
let pos: FieldPosition;
|
||||||
|
switch (index) {
|
||||||
|
case BattlerIndex.ATTACKER:
|
||||||
|
throw new Error("Cannot convert BattlerIndex.ATTACKER to a field position!")
|
||||||
|
case BattlerIndex.PLAYER:
|
||||||
|
case BattlerIndex.ENEMY:
|
||||||
|
pos = FieldPosition.LEFT;
|
||||||
|
break;
|
||||||
|
case BattlerIndex.PLAYER_2:
|
||||||
|
case BattlerIndex.ENEMY_2:
|
||||||
|
pos = FieldPosition.RIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// In single battles, left positions become center
|
||||||
|
if (!globalScene.currentBattle.double && pos === FieldPosition.LEFT) {
|
||||||
|
pos = FieldPosition.CENTER
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
@ -43,5 +43,6 @@ export enum UiMode {
|
|||||||
TEST_DIALOGUE,
|
TEST_DIALOGUE,
|
||||||
AUTO_COMPLETE,
|
AUTO_COMPLETE,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
MYSTERY_ENCOUNTER
|
MYSTERY_ENCOUNTER,
|
||||||
|
CHANGE_PASSWORD_FORM,
|
||||||
}
|
}
|
||||||
|
@ -1,109 +1,187 @@
|
|||||||
|
import type { SerializedPositionalTag } from "#data/positional-tags/load-positional-tag";
|
||||||
|
// biome-ignore lint/correctness/noUnusedImports: TSDoc
|
||||||
|
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
||||||
import type { TerrainType } from "#data/terrain";
|
import type { TerrainType } from "#data/terrain";
|
||||||
import type { ArenaTagSide } from "#enums/arena-tag-side";
|
import type { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import type { ArenaTagType } from "#enums/arena-tag-type";
|
import type { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import type { WeatherType } from "#enums/weather-type";
|
import type { WeatherType } from "#enums/weather-type";
|
||||||
|
|
||||||
/** Alias for all {@linkcode ArenaEvent} type strings */
|
/** Enum representing the types of all {@linkcode ArenaEvent}s that can be emitted. */
|
||||||
export enum ArenaEventType {
|
export enum ArenaEventType {
|
||||||
/** Triggers when a {@linkcode WeatherType} is added, overlapped, or removed */
|
/** Emitted when a {@linkcode WeatherType} is added, overlapped, or removed */
|
||||||
WEATHER_CHANGED = "onWeatherChanged",
|
WEATHER_CHANGED = "onWeatherChanged",
|
||||||
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
/** Emitted when a {@linkcode TerrainType} is added, overlapped, or removed */
|
||||||
TERRAIN_CHANGED = "onTerrainChanged",
|
TERRAIN_CHANGED = "onTerrainChanged",
|
||||||
|
|
||||||
/** Triggers when a {@linkcode ArenaTagType} is added */
|
/** Emitted when a new {@linkcode ArenaTag} is added */
|
||||||
TAG_ADDED = "onTagAdded",
|
ARENA_TAG_ADDED = "onArenaTagAdded",
|
||||||
/** Triggers when a {@linkcode ArenaTagType} is removed */
|
/** Emitted when an existing {@linkcode ArenaTag} is removed */
|
||||||
TAG_REMOVED = "onTagRemoved",
|
ARENA_TAG_REMOVED = "onArenaTagRemoved",
|
||||||
|
|
||||||
|
/** Emitted when a new {@linkcode PositionalTag} is added */
|
||||||
|
POSITIONAL_TAG_ADDED = "onPositionalTagAdded",
|
||||||
|
/** Emitted when an existing {@linkcode PositionalTag} is removed */
|
||||||
|
POSITIONAL_TAG_REMOVED = "onPositionalTagRemoved",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base container class for all {@linkcode ArenaEventType} events
|
* Abstract container class for all {@linkcode ArenaEventType} events.
|
||||||
* @extends Event
|
|
||||||
*/
|
*/
|
||||||
export class ArenaEvent extends Event {
|
abstract class ArenaEvent extends Event {
|
||||||
/** The total duration of the {@linkcode ArenaEventType} */
|
/** The {@linkcode ArenaEventType} being emitted. */
|
||||||
public duration: number;
|
public declare abstract readonly type: ArenaEventType; // that's a mouthful!
|
||||||
constructor(eventType: ArenaEventType, duration: number) {
|
// biome-ignore lint/complexity/noUselessConstructor: changes the type of the type field
|
||||||
super(eventType);
|
constructor(type: ArenaEventType) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { ArenaEvent };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events. \
|
||||||
|
* Emitted whenever a weather effect starts, ends or is replaced.
|
||||||
|
*/
|
||||||
|
export class WeatherChangedEvent extends ArenaEvent {
|
||||||
|
declare type: ArenaEventType.WEATHER_CHANGED;
|
||||||
|
|
||||||
|
/** The new {@linkcode WeatherType} being set. */
|
||||||
|
public weatherType: WeatherType;
|
||||||
|
/**
|
||||||
|
* The new weather's initial duration.
|
||||||
|
* Unused if {@linkcode weatherType} is set to {@linkcode WeatherType.NONE}.
|
||||||
|
*/
|
||||||
|
public duration: number;
|
||||||
|
|
||||||
|
constructor(weatherType: WeatherType, duration: number) {
|
||||||
|
super(ArenaEventType.WEATHER_CHANGED);
|
||||||
|
|
||||||
|
this.weatherType = weatherType;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events
|
|
||||||
* @extends ArenaEvent
|
|
||||||
*/
|
|
||||||
export class WeatherChangedEvent extends ArenaEvent {
|
|
||||||
/** The {@linkcode WeatherType} being overridden */
|
|
||||||
public oldWeatherType: WeatherType;
|
|
||||||
/** The {@linkcode WeatherType} being set */
|
|
||||||
public newWeatherType: WeatherType;
|
|
||||||
constructor(oldWeatherType: WeatherType, newWeatherType: WeatherType, duration: number) {
|
|
||||||
super(ArenaEventType.WEATHER_CHANGED, duration);
|
|
||||||
|
|
||||||
this.oldWeatherType = oldWeatherType;
|
|
||||||
this.newWeatherType = newWeatherType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events
|
* Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events. \
|
||||||
* @extends ArenaEvent
|
* Emitted whenever a terrain effect starts, ends or is replaced.
|
||||||
*/
|
*/
|
||||||
export class TerrainChangedEvent extends ArenaEvent {
|
export class TerrainChangedEvent extends ArenaEvent {
|
||||||
/** The {@linkcode TerrainType} being overridden */
|
declare type: ArenaEventType.TERRAIN_CHANGED;
|
||||||
public oldTerrainType: TerrainType;
|
|
||||||
/** The {@linkcode TerrainType} being set */
|
|
||||||
public newTerrainType: TerrainType;
|
|
||||||
constructor(oldTerrainType: TerrainType, newTerrainType: TerrainType, duration: number) {
|
|
||||||
super(ArenaEventType.TERRAIN_CHANGED, duration);
|
|
||||||
|
|
||||||
this.oldTerrainType = oldTerrainType;
|
/** The new {@linkcode TerrainType} being set. */
|
||||||
this.newTerrainType = newTerrainType;
|
public terrainType: TerrainType;
|
||||||
|
/**
|
||||||
|
* The new terrain's initial duration.
|
||||||
|
* Unused if {@linkcode terrainType} is set to {@linkcode TerrainType.NONE}.
|
||||||
|
*/
|
||||||
|
public duration: number;
|
||||||
|
|
||||||
|
constructor(terrainType: TerrainType, duration: number) {
|
||||||
|
super(ArenaEventType.TERRAIN_CHANGED);
|
||||||
|
|
||||||
|
this.terrainType = terrainType;
|
||||||
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container class for {@linkcode ArenaEventType.TAG_ADDED} events
|
* Container class for {@linkcode ArenaEventType.ARENA_TAG_ADDED} events. \
|
||||||
* @extends ArenaEvent
|
* Emitted whenever a new {@linkcode ArenaTag} is added to the arena, or whenever an existing
|
||||||
|
* {@linkcode ArenaTrapTag} overlaps and adds new layers.
|
||||||
*/
|
*/
|
||||||
export class TagAddedEvent extends ArenaEvent {
|
export class ArenaTagAddedEvent extends ArenaEvent {
|
||||||
/** The {@linkcode ArenaTagType} being added */
|
declare type: ArenaEventType.ARENA_TAG_ADDED;
|
||||||
public arenaTagType: ArenaTagType;
|
|
||||||
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
/** The {@linkcode ArenaTagType} of the tag being added */
|
||||||
public arenaTagSide: ArenaTagSide;
|
public tagType: ArenaTagType;
|
||||||
/** The current number of layers of the arena trap. */
|
/** The {@linkcode ArenaTagSide} the tag is being added too */
|
||||||
public arenaTagLayers: number;
|
public side: ArenaTagSide;
|
||||||
/** The maximum amount of layers of the arena trap. */
|
/** The tag's initial duration. */
|
||||||
public arenaTagMaxLayers: number;
|
public duration: number;
|
||||||
|
/**
|
||||||
|
* A tuple containing the current and maximum number of layers of the current {@linkcode ArenaTrapTag},
|
||||||
|
* or `undefined` if the tag was not a trap.
|
||||||
|
*/
|
||||||
|
public trapLayers: [current: number, max: number] | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
arenaTagType: ArenaTagType,
|
side: ArenaTagType,
|
||||||
arenaTagSide: ArenaTagSide,
|
arenaTagSide: ArenaTagSide,
|
||||||
duration: number,
|
duration: number,
|
||||||
arenaTagLayers?: number,
|
trapLayers?: [current: number, max: number],
|
||||||
arenaTagMaxLayers?: number,
|
|
||||||
) {
|
) {
|
||||||
super(ArenaEventType.TAG_ADDED, duration);
|
super(ArenaEventType.ARENA_TAG_ADDED);
|
||||||
|
|
||||||
this.arenaTagType = arenaTagType;
|
this.tagType = side;
|
||||||
this.arenaTagSide = arenaTagSide;
|
this.side = arenaTagSide;
|
||||||
this.arenaTagLayers = arenaTagLayers!; // TODO: is this bang correct?
|
this.duration = duration;
|
||||||
this.arenaTagMaxLayers = arenaTagMaxLayers!; // TODO: is this bang correct?
|
this.trapLayers = trapLayers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container class for {@linkcode ArenaEventType.TAG_REMOVED} events
|
* Container class for {@linkcode ArenaEventType.ARENA_TAG_REMOVED} events. \
|
||||||
* @extends ArenaEvent
|
* Emitted whenever an {@linkcode ArenaTag} is removed from the field for any reason.
|
||||||
*/
|
*/
|
||||||
export class TagRemovedEvent extends ArenaEvent {
|
export class ArenaTagRemovedEvent extends ArenaEvent {
|
||||||
/** The {@linkcode ArenaTagType} being removed */
|
declare type: ArenaEventType.ARENA_TAG_REMOVED;
|
||||||
public arenaTagType: ArenaTagType;
|
|
||||||
/** The {@linkcode ArenaTagSide} the tag was being placed on */
|
|
||||||
public arenaTagSide: ArenaTagSide;
|
|
||||||
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
|
||||||
super(ArenaEventType.TAG_REMOVED, duration);
|
|
||||||
|
|
||||||
this.arenaTagType = arenaTagType;
|
/** The {@linkcode ArenaTagType} of the tag being removed. */
|
||||||
this.arenaTagSide = arenaTagSide;
|
public tagType: ArenaTagType;
|
||||||
|
/** The {@linkcode ArenaTagSide} the removed tag affected. */
|
||||||
|
public side: ArenaTagSide;
|
||||||
|
|
||||||
|
constructor(tagType: ArenaTagType, side: ArenaTagSide) {
|
||||||
|
super(ArenaEventType.ARENA_TAG_REMOVED);
|
||||||
|
|
||||||
|
this.tagType = tagType;
|
||||||
|
this.side = side;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.POSITIONAL_TAG_ADDED} events. \
|
||||||
|
* Emitted whenever a new {@linkcode PositionalTag} is spawned and added to the arena.
|
||||||
|
*/
|
||||||
|
export class PositionalTagAddedEvent extends ArenaEvent {
|
||||||
|
declare type: ArenaEventType.POSITIONAL_TAG_ADDED;
|
||||||
|
|
||||||
|
/** The {@linkcode SerializedPositionalTag} being added to the arena. */
|
||||||
|
public tag: SerializedPositionalTag;
|
||||||
|
|
||||||
|
/** The {@linkcode PositionalTagType} of the tag being added. */
|
||||||
|
public tagType: PositionalTagType;
|
||||||
|
/** The {@linkcode BattlerIndex} targeted by the newly created tag. */
|
||||||
|
public targetIndex: BattlerIndex;
|
||||||
|
/** The tag's current duration. */
|
||||||
|
public duration: number;
|
||||||
|
|
||||||
|
constructor(tag: SerializedPositionalTag) {
|
||||||
|
super(ArenaEventType.POSITIONAL_TAG_ADDED);
|
||||||
|
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.POSITIONAL_TAG_REMOVED} events. \
|
||||||
|
* Emitted whenever a currently-active {@linkcode PositionalTag} triggers (or disappears)
|
||||||
|
* and is removed from the arena.
|
||||||
|
*/
|
||||||
|
export class PositionalTagRemovedEvent extends ArenaEvent {
|
||||||
|
declare type: ArenaEventType.POSITIONAL_TAG_REMOVED;
|
||||||
|
|
||||||
|
/** The {@linkcode PositionalTagType} of the tag being deleted. */
|
||||||
|
public tagType: PositionalTagType;
|
||||||
|
/** The {@linkcode BattlerIndex} targeted by the newly removed tag. */
|
||||||
|
public targetIndex: BattlerIndex;
|
||||||
|
|
||||||
|
constructor(tagType: PositionalTagType, targetIndex: BattlerIndex) {
|
||||||
|
super(ArenaEventType.POSITIONAL_TAG_ADDED);
|
||||||
|
|
||||||
|
this.tagType = tagType;
|
||||||
|
this.targetIndex = targetIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,18 @@ import { SpeciesId } from "#enums/species-id";
|
|||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena";
|
import {
|
||||||
|
type ArenaEvent,
|
||||||
|
ArenaTagAddedEvent,
|
||||||
|
ArenaTagRemovedEvent,
|
||||||
|
TerrainChangedEvent,
|
||||||
|
WeatherChangedEvent,
|
||||||
|
} from "#events/arena";
|
||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import { FieldEffectModifier } from "#modifiers/modifier";
|
import { FieldEffectModifier } from "#modifiers/modifier";
|
||||||
import type { Move } from "#moves/move";
|
import type { Move } from "#moves/move";
|
||||||
import type { AbstractConstructor } from "#types/type-helpers";
|
import type { AbstractConstructor } from "#types/type-helpers";
|
||||||
|
import type { TypedEventTarget } from "#types/typed-event-target";
|
||||||
import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common";
|
import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common";
|
||||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||||
|
|
||||||
@ -45,10 +52,7 @@ export class Arena {
|
|||||||
public terrain: Terrain | null;
|
public terrain: Terrain | null;
|
||||||
/** All currently-active {@linkcode ArenaTag}s on both sides of the field. */
|
/** All currently-active {@linkcode ArenaTag}s on both sides of the field. */
|
||||||
public tags: ArenaTag[] = [];
|
public tags: ArenaTag[] = [];
|
||||||
/**
|
/** A manager for the currently-active {@linkcode PositionalTag}s on both sides of the field. */
|
||||||
* All currently-active {@linkcode PositionalTag}s on both sides of the field,
|
|
||||||
* sorted by tag type.
|
|
||||||
*/
|
|
||||||
public positionalTagManager: PositionalTagManager = new PositionalTagManager();
|
public positionalTagManager: PositionalTagManager = new PositionalTagManager();
|
||||||
|
|
||||||
public bgm: string;
|
public bgm: string;
|
||||||
@ -66,7 +70,11 @@ export class Arena {
|
|||||||
private pokemonPool: PokemonPools;
|
private pokemonPool: PokemonPools;
|
||||||
private trainerPool: BiomeTierTrainerPools;
|
private trainerPool: BiomeTierTrainerPools;
|
||||||
|
|
||||||
public readonly eventTarget: EventTarget = new EventTarget();
|
/**
|
||||||
|
* Event dispatcher for various {@linkcode ArenaEvent}s.
|
||||||
|
* Used primarily to update the arena flyout.
|
||||||
|
*/
|
||||||
|
public readonly eventTarget: TypedEventTarget<ArenaEvent> = new EventTarget();
|
||||||
|
|
||||||
constructor(biome: BiomeId, bgm: string, playerFaints = 0) {
|
constructor(biome: BiomeId, bgm: string, playerFaints = 0) {
|
||||||
this.biomeType = biome;
|
this.biomeType = biome;
|
||||||
@ -145,7 +153,7 @@ export class Arena {
|
|||||||
? BiomePoolTier.BOSS_SUPER_RARE
|
? BiomePoolTier.BOSS_SUPER_RARE
|
||||||
: BiomePoolTier.BOSS_ULTRA_RARE;
|
: BiomePoolTier.BOSS_ULTRA_RARE;
|
||||||
console.log(BiomePoolTier[tier]);
|
console.log(BiomePoolTier[tier]);
|
||||||
while (!this.pokemonPool[tier].length) {
|
while (!this.pokemonPool[tier]?.length) {
|
||||||
console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
|
console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
|
||||||
tier--;
|
tier--;
|
||||||
}
|
}
|
||||||
@ -345,9 +353,7 @@ export class Arena {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.weather = weather ? new Weather(weather, weatherDuration.value) : null;
|
this.weather = weather ? new Weather(weather, weatherDuration.value) : null;
|
||||||
this.eventTarget.dispatchEvent(
|
this.eventTarget.dispatchEvent(new WeatherChangedEvent(this.getWeatherType(), weatherDuration.value));
|
||||||
new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!),
|
|
||||||
); // TODO: is this bang correct?
|
|
||||||
|
|
||||||
if (this.weather) {
|
if (this.weather) {
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
@ -433,9 +439,7 @@ export class Arena {
|
|||||||
|
|
||||||
this.terrain = terrain ? new Terrain(terrain, terrainDuration.value) : null;
|
this.terrain = terrain ? new Terrain(terrain, terrainDuration.value) : null;
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(
|
this.eventTarget.dispatchEvent(new TerrainChangedEvent(this.getTerrainType(), terrainDuration.value));
|
||||||
new TerrainChangedEvent(oldTerrainType, this.terrain?.terrainType!, this.terrain?.turnsLeft!),
|
|
||||||
); // TODO: are those bangs correct?
|
|
||||||
|
|
||||||
if (this.terrain) {
|
if (this.terrain) {
|
||||||
if (!ignoreAnim) {
|
if (!ignoreAnim) {
|
||||||
@ -708,26 +712,25 @@ export class Arena {
|
|||||||
const existingTag = this.getTagOnSide(tagType, side);
|
const existingTag = this.getTagOnSide(tagType, side);
|
||||||
if (existingTag) {
|
if (existingTag) {
|
||||||
existingTag.onOverlap(this, globalScene.getPokemonById(sourceId));
|
existingTag.onOverlap(this, globalScene.getPokemonById(sourceId));
|
||||||
|
|
||||||
if (existingTag instanceof ArenaTrapTag) {
|
|
||||||
const { tagType, side, turnCount, layers, maxLayers } = existingTag as ArenaTrapTag;
|
|
||||||
this.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new tag object
|
// creates a new tag object
|
||||||
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, side);
|
const newTag = getArenaTag(tagType, turnCount, sourceMove, sourceId, side);
|
||||||
if (newTag) {
|
if (!newTag) {
|
||||||
newTag.onAdd(this, quiet);
|
return false;
|
||||||
this.tags.push(newTag);
|
}
|
||||||
|
|
||||||
const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {};
|
newTag.onAdd(this, quiet);
|
||||||
|
this.tags.push(newTag);
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(
|
// Dispatch a TagAddedEvent to update the flyout.
|
||||||
new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers),
|
if (newTag instanceof ArenaTrapTag) {
|
||||||
|
globalScene.arena.eventTarget.dispatchEvent(
|
||||||
|
new ArenaTagAddedEvent(tagType, side, turnCount, [newTag.layers, newTag.maxLayers]),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
globalScene.arena.eventTarget.dispatchEvent(new ArenaTagAddedEvent(tagType, side, turnCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -807,7 +810,7 @@ export class Arena {
|
|||||||
t.onRemove(this);
|
t.onRemove(this);
|
||||||
this.tags.splice(this.tags.indexOf(t), 1);
|
this.tags.splice(this.tags.indexOf(t), 1);
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(new TagRemovedEvent(t.tagType, t.side, t.turnCount));
|
this.eventTarget.dispatchEvent(new ArenaTagRemovedEvent(t.tagType, t.side));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,7 +821,7 @@ export class Arena {
|
|||||||
tag.onRemove(this);
|
tag.onRemove(this);
|
||||||
tags.splice(tags.indexOf(tag), 1);
|
tags.splice(tags.indexOf(tag), 1);
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
this.eventTarget.dispatchEvent(new ArenaTagRemovedEvent(tag.tagType, tag.side));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
@ -829,20 +832,17 @@ export class Arena {
|
|||||||
tag.onRemove(this, quiet);
|
tag.onRemove(this, quiet);
|
||||||
this.tags.splice(this.tags.indexOf(tag), 1);
|
this.tags.splice(this.tags.indexOf(tag), 1);
|
||||||
|
|
||||||
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
this.eventTarget.dispatchEvent(new ArenaTagRemovedEvent(tag.tagType, tag.side));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllTags(): void {
|
removeAllTags(): void {
|
||||||
while (this.tags.length) {
|
for (const tag of this.tags) {
|
||||||
this.tags[0].onRemove(this);
|
tag.onRemove(this);
|
||||||
this.eventTarget.dispatchEvent(
|
this.eventTarget.dispatchEvent(new ArenaTagRemovedEvent(tag.tagType, tag.side));
|
||||||
new TagRemovedEvent(this.tags[0].tagType, this.tags[0].side, this.tags[0].turnCount),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tags.splice(0, 1);
|
|
||||||
}
|
}
|
||||||
|
this.tags = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ApiBase } from "#api/api-base";
|
import { ApiBase } from "#api/api-base";
|
||||||
import { SESSION_ID_COOKIE_NAME } from "#app/constants";
|
import { SESSION_ID_COOKIE_NAME } from "#app/constants";
|
||||||
import type {
|
import type {
|
||||||
|
AccountChangePwRequest,
|
||||||
AccountInfoResponse,
|
AccountInfoResponse,
|
||||||
AccountLoginRequest,
|
AccountLoginRequest,
|
||||||
AccountLoginResponse,
|
AccountLoginResponse,
|
||||||
@ -95,4 +96,19 @@ export class PokerogueAccountApi extends ApiBase {
|
|||||||
|
|
||||||
removeCookie(SESSION_ID_COOKIE_NAME); // we are always clearing the cookie.
|
removeCookie(SESSION_ID_COOKIE_NAME); // we are always clearing the cookie.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async changePassword(changePwData: AccountChangePwRequest) {
|
||||||
|
try {
|
||||||
|
const response = await this.doPost("/account/changepw", changePwData, "form-urlencoded");
|
||||||
|
if (response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
console.warn("Change password failed!", response.status, response.statusText);
|
||||||
|
return response.text();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Change password failed!", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown error!";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import { TrainerVariant } from "#enums/trainer-variant";
|
|||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { Unlockables } from "#enums/unlockables";
|
import { Unlockables } from "#enums/unlockables";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena";
|
import { ArenaTagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena";
|
||||||
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
||||||
// biome-ignore lint/performance/noNamespaceImport: Something weird is going on here and I don't want to touch it
|
// biome-ignore lint/performance/noNamespaceImport: Something weird is going on here and I don't want to touch it
|
||||||
import * as Modifier from "#modifiers/modifier";
|
import * as Modifier from "#modifiers/modifier";
|
||||||
@ -1064,36 +1064,30 @@ export class GameData {
|
|||||||
});
|
});
|
||||||
|
|
||||||
globalScene.arena.weather = sessionData.arena.weather;
|
globalScene.arena.weather = sessionData.arena.weather;
|
||||||
globalScene.arena.eventTarget.dispatchEvent(
|
if (globalScene.arena.getWeatherType() !== WeatherType.NONE) {
|
||||||
new WeatherChangedEvent(
|
globalScene.arena.eventTarget.dispatchEvent(
|
||||||
WeatherType.NONE,
|
new WeatherChangedEvent(globalScene.arena.getWeatherType(), globalScene.arena.weather?.turnsLeft!),
|
||||||
globalScene.arena.weather?.weatherType!,
|
);
|
||||||
globalScene.arena.weather?.turnsLeft!,
|
}
|
||||||
),
|
|
||||||
); // TODO: is this bang correct?
|
|
||||||
|
|
||||||
globalScene.arena.terrain = sessionData.arena.terrain;
|
globalScene.arena.terrain = sessionData.arena.terrain;
|
||||||
globalScene.arena.eventTarget.dispatchEvent(
|
if (globalScene.arena.getTerrainType() !== TerrainType.NONE) {
|
||||||
new TerrainChangedEvent(
|
globalScene.arena.eventTarget.dispatchEvent(
|
||||||
TerrainType.NONE,
|
new TerrainChangedEvent(globalScene.arena.getTerrainType(), globalScene.arena.terrain?.turnsLeft!),
|
||||||
globalScene.arena.terrain?.terrainType!,
|
);
|
||||||
globalScene.arena.terrain?.turnsLeft!,
|
}
|
||||||
),
|
|
||||||
); // TODO: is this bang correct?
|
|
||||||
|
|
||||||
globalScene.arena.playerTerasUsed = sessionData.arena.playerTerasUsed;
|
globalScene.arena.playerTerasUsed = sessionData.arena.playerTerasUsed;
|
||||||
|
|
||||||
globalScene.arena.tags = sessionData.arena.tags;
|
globalScene.arena.tags = sessionData.arena.tags;
|
||||||
if (globalScene.arena.tags) {
|
for (const tag of globalScene.arena.tags) {
|
||||||
for (const tag of globalScene.arena.tags) {
|
if (tag instanceof ArenaTrapTag) {
|
||||||
if (tag instanceof ArenaTrapTag) {
|
const { tagType, side, turnCount, layers, maxLayers } = tag;
|
||||||
const { tagType, side, turnCount, layers, maxLayers } = tag as ArenaTrapTag;
|
globalScene.arena.eventTarget.dispatchEvent(
|
||||||
globalScene.arena.eventTarget.dispatchEvent(
|
new ArenaTagAddedEvent(tagType, side, turnCount, [layers, maxLayers]),
|
||||||
new TagAddedEvent(tagType, side, turnCount, layers, maxLayers),
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
globalScene.arena.eventTarget.dispatchEvent(new ArenaTagAddedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
globalScene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,61 +1,124 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { ArenaTrapTag } from "#data/arena-tag";
|
// biome-ignore-start lint/correctness/noUnusedImports: TSDocs
|
||||||
import { TerrainType } from "#data/terrain";
|
import type { ArenaTag } from "#data/arena-tag";
|
||||||
|
import type { SerializedPositionalTag } from "#data/positional-tags/load-positional-tag";
|
||||||
|
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
||||||
|
import { type Terrain, TerrainType } from "#data/terrain";
|
||||||
|
import type { Weather } from "#data/weather";
|
||||||
|
// biome-ignore-end lint/correctness/noUnusedImports: TSDocs
|
||||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import { battlerIndexToFieldPosition, FieldPosition } from "#enums/field-position";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import { TextStyle } from "#enums/text-style";
|
import { TextStyle } from "#enums/text-style";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import type { ArenaEvent } from "#events/arena";
|
|
||||||
import {
|
import {
|
||||||
ArenaEventType,
|
ArenaEventType,
|
||||||
TagAddedEvent,
|
type ArenaTagAddedEvent,
|
||||||
TagRemovedEvent,
|
type ArenaTagRemovedEvent,
|
||||||
TerrainChangedEvent,
|
type PositionalTagAddedEvent,
|
||||||
WeatherChangedEvent,
|
type PositionalTagRemovedEvent,
|
||||||
|
type TerrainChangedEvent,
|
||||||
|
type WeatherChangedEvent,
|
||||||
} from "#events/arena";
|
} from "#events/arena";
|
||||||
import type { TurnEndEvent } from "#events/battle-scene";
|
|
||||||
import { BattleSceneEventType } from "#events/battle-scene";
|
import { BattleSceneEventType } from "#events/battle-scene";
|
||||||
import { addTextObject } from "#ui/text";
|
import { addTextObject } from "#ui/text";
|
||||||
import { TimeOfDayWidget } from "#ui/time-of-day-widget";
|
import { TimeOfDayWidget } from "#ui/time-of-day-widget";
|
||||||
import { addWindow, WindowVariant } from "#ui/ui-theme";
|
import { addWindow, WindowVariant } from "#ui/ui-theme";
|
||||||
import { fixedInt } from "#utils/common";
|
import { fixedInt } from "#utils/common";
|
||||||
import { toCamelCase, toTitleCase } from "#utils/strings";
|
import { toCamelCase } from "#utils/strings";
|
||||||
import type { ParseKeys } from "i18next";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** Enum used to differentiate {@linkcode Arena} effects */
|
// #region Interfaces
|
||||||
enum ArenaEffectType {
|
|
||||||
PLAYER,
|
|
||||||
WEATHER,
|
|
||||||
TERRAIN,
|
|
||||||
FIELD,
|
|
||||||
ENEMY,
|
|
||||||
}
|
|
||||||
/** Container for info about an {@linkcode Arena}'s effects */
|
|
||||||
interface ArenaEffectInfo {
|
|
||||||
/** The enum string representation of the effect */
|
|
||||||
name: string;
|
|
||||||
/** {@linkcode ArenaEffectType} type of effect */
|
|
||||||
effecType: ArenaEffectType;
|
|
||||||
|
|
||||||
/** The maximum duration set by the effect */
|
/** Base container for info about the currently active {@linkcode Weather}. */
|
||||||
|
interface WeatherInfo {
|
||||||
|
/** The localized name of the weather. */
|
||||||
|
name: string;
|
||||||
|
/** The initial duration of the weather effect, or `0` if it should last indefinitely. */
|
||||||
maxDuration: number;
|
maxDuration: number;
|
||||||
/** The current duration left on the effect */
|
/** The current duration left on the weather. */
|
||||||
duration: number;
|
duration: number;
|
||||||
/** The arena tag type being added */
|
/** The current {@linkcode WeatherType}. */
|
||||||
|
weatherType: WeatherType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base container for info about the currently active {@linkcode Terrain}. */
|
||||||
|
interface TerrainInfo {
|
||||||
|
/** The localized name of the terrain. */
|
||||||
|
name: string;
|
||||||
|
/** The initial duration of the terrain effect, or `0` if it should last indefinitely. */
|
||||||
|
maxDuration: number;
|
||||||
|
/** The current duration left on the terrain. */
|
||||||
|
duration: number;
|
||||||
|
/** The current {@linkcode TerrainType}. */
|
||||||
|
terrainType: TerrainType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interface for info about an {@linkcode ArenaTag}'s effects */
|
||||||
|
interface ArenaTagInfo {
|
||||||
|
/** The localized name of the tag. */
|
||||||
|
name: string;
|
||||||
|
/** The {@linkcode ArenaTagSide} that the tag applies to. */
|
||||||
|
side: ArenaTagSide;
|
||||||
|
/** The maximum duration of the tag, or `0` if it should last indefinitely. */
|
||||||
|
maxDuration: number;
|
||||||
|
/** The current duration left on the tag. */
|
||||||
|
duration: number;
|
||||||
|
/** The tag's {@linkcode ArenaTagType}. */
|
||||||
tagType?: ArenaTagType;
|
tagType?: ArenaTagType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFieldEffectText(arenaTagType: string): string {
|
/** Container for info about pending {@linkcode PositionalTag}s. */
|
||||||
if (!arenaTagType || arenaTagType === ArenaTagType.NONE) {
|
interface PositionalTagInfo {
|
||||||
return arenaTagType;
|
/** The localized name of the effect. */
|
||||||
}
|
name: string;
|
||||||
const effectName = toCamelCase(arenaTagType);
|
/** The {@linkcode BattlerIndex} that the effect is slated to affect. */
|
||||||
const i18nKey = `arenaFlyout:${effectName}` as ParseKeys;
|
targetIndex: BattlerIndex;
|
||||||
const resultName = i18next.t(i18nKey);
|
/** The current duration of the effect. */
|
||||||
return !resultName || resultName === i18nKey ? toTitleCase(arenaTagType) : resultName;
|
duration: number;
|
||||||
|
/** The tag's {@linkcode PositionalTagType}. */
|
||||||
|
tagType: PositionalTagType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #endregion interfaces
|
||||||
|
|
||||||
|
// #region String functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the localized text for a given effect.
|
||||||
|
* @param text - The raw text of the effect; assumed to be in `UPPER_SNAKE_CASE` from a reverse mapping.
|
||||||
|
* @returns The localized text for the effect.
|
||||||
|
*/
|
||||||
|
function localizeEffectName(text: string): string {
|
||||||
|
const effectName = toCamelCase(text);
|
||||||
|
const i18nKey = `arenaFlyout:${effectName}`;
|
||||||
|
const resultName = i18next.t(i18nKey);
|
||||||
|
return resultName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the localized name of a given {@linkcode PositionalTag}.
|
||||||
|
* @param tag - The raw serialized data for the given tag
|
||||||
|
* @returns The localized text to be displayed on-screen.
|
||||||
|
*/
|
||||||
|
function getPositionalTagDisplayName(tag: SerializedPositionalTag): string {
|
||||||
|
let tagName: string;
|
||||||
|
if ("sourceMove" in tag) {
|
||||||
|
// Delayed attacks will use the source move's name
|
||||||
|
tagName = MoveId[tag.sourceMove];
|
||||||
|
} else {
|
||||||
|
tagName = PositionalTagType[tag.tagType];
|
||||||
|
}
|
||||||
|
|
||||||
|
return localizeEffectName(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to display and update the on-screen arena flyout.
|
||||||
|
*/
|
||||||
export class ArenaFlyout extends Phaser.GameObjects.Container {
|
export class ArenaFlyout extends Phaser.GameObjects.Container {
|
||||||
/** The restricted width of the flyout which should be drawn to */
|
/** The restricted width of the flyout which should be drawn to */
|
||||||
private flyoutWidth = 170;
|
private flyoutWidth = 170;
|
||||||
@ -88,24 +151,36 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
private flyoutTextHeaderPlayer: Phaser.GameObjects.Text;
|
private flyoutTextHeaderPlayer: Phaser.GameObjects.Text;
|
||||||
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the enemy's effects */
|
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the enemy's effects */
|
||||||
private flyoutTextHeaderEnemy: Phaser.GameObjects.Text;
|
private flyoutTextHeaderEnemy: Phaser.GameObjects.Text;
|
||||||
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate field effects */
|
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate neutral effects */
|
||||||
private flyoutTextHeaderField: Phaser.GameObjects.Text;
|
private flyoutTextHeaderField: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the player's effects */
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the player's effects */
|
||||||
private flyoutTextPlayer: Phaser.GameObjects.Text;
|
private flyoutTextPlayer: Phaser.GameObjects.Text;
|
||||||
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the enemy's effects */
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the enemy's effects */
|
||||||
private flyoutTextEnemy: Phaser.GameObjects.Text;
|
private flyoutTextEnemy: Phaser.GameObjects.Text;
|
||||||
/** The {@linkcode Phaser.GameObjects.Text} used to indicate field effects */
|
/** The {@linkcode Phaser.GameObjects.Text} used to indicate neutral effects */
|
||||||
private flyoutTextField: Phaser.GameObjects.Text;
|
private flyoutTextField: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
/** Container for all field effects observed by this object */
|
/** Holds info about the current active {@linkcode Weather}, if any are active. */
|
||||||
private readonly fieldEffectInfo: ArenaEffectInfo[] = [];
|
private weatherInfo?: WeatherInfo;
|
||||||
|
/** Holds info about the current active {@linkcode Terrain}, if any are active. */
|
||||||
|
private terrainInfo?: TerrainInfo;
|
||||||
|
|
||||||
// Stores callbacks in a variable so they can be unsubscribed from when destroyed
|
/** Container for all {@linkcode ArenaTag}s observed by this object. */
|
||||||
private readonly onNewArenaEvent = (event: Event) => this.onNewArena(event);
|
private arenaTags: ArenaTagInfo[] = [];
|
||||||
private readonly onTurnEndEvent = (event: Event) => this.onTurnEnd(event);
|
/** Container for all {@linkcode PositionalTag}s observed by this object. */
|
||||||
|
private positionalTags: PositionalTagInfo[] = [];
|
||||||
|
|
||||||
private readonly onFieldEffectChangedEvent = (event: Event) => this.onFieldEffectChanged(event);
|
// Store callbacks in variables so they can be unsubscribed from when destroyed
|
||||||
|
private readonly onNewArenaEvent = () => this.onNewArena();
|
||||||
|
private readonly onTurnEndEvent = () => this.onTurnEnd();
|
||||||
|
private readonly onWeatherChangedEvent = (event: WeatherChangedEvent) => this.onWeatherChanged(event);
|
||||||
|
private readonly onTerrainChangedEvent = (event: TerrainChangedEvent) => this.onTerrainChanged(event);
|
||||||
|
private readonly onArenaTagAddedEvent = (event: ArenaTagAddedEvent) => this.onArenaTagAdded(event);
|
||||||
|
private readonly onArenaTagRemovedEvent = (event: ArenaTagRemovedEvent) => this.onArenaTagRemoved(event);
|
||||||
|
private readonly onPositionalTagAddedEvent = (event: PositionalTagAddedEvent) => this.onPositionalTagAdded(event);
|
||||||
|
// biome-ignore format: Keeps lines in 1 piece
|
||||||
|
private readonly onPositionalTagRemovedEvent = (event: PositionalTagRemovedEvent) => this.onPositionalTagRemoved(event);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(globalScene, 0, 0);
|
super(globalScene, 0, 0);
|
||||||
@ -165,7 +240,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
this.flyoutTextHeaderField = addTextObject(
|
this.flyoutTextHeaderField = addTextObject(
|
||||||
this.flyoutWidth / 2,
|
this.flyoutWidth / 2,
|
||||||
5,
|
5,
|
||||||
i18next.t("arenaFlyout:field"),
|
i18next.t("arenaFlyout:neutral"),
|
||||||
TextStyle.SUMMARY_GREEN,
|
TextStyle.SUMMARY_GREEN,
|
||||||
);
|
);
|
||||||
this.flyoutTextHeaderField.setFontSize(54);
|
this.flyoutTextHeaderField.setFontSize(54);
|
||||||
@ -213,202 +288,185 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
this.name = "Fight Flyout";
|
this.name = "Fight Flyout";
|
||||||
this.flyoutParent.name = "Fight Flyout Parent";
|
this.flyoutParent.name = "Fight Flyout Parent";
|
||||||
|
|
||||||
// Subscribes to required events available on game start
|
// Subscribe to required events available on game start
|
||||||
globalScene.eventTarget.addEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
globalScene.eventTarget.addEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
||||||
globalScene.eventTarget.addEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
globalScene.eventTarget.addEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onNewArena(_event: Event) {
|
/**
|
||||||
this.fieldEffectInfo.length = 0;
|
* Initialize listeners upon creating a new arena.
|
||||||
|
*/
|
||||||
|
private onNewArena() {
|
||||||
|
this.arenaTags = [];
|
||||||
|
this.positionalTags = [];
|
||||||
|
|
||||||
// Subscribes to required events available on battle start
|
// Subscribe to required events available on battle start
|
||||||
globalScene.arena.eventTarget.addEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
|
// biome-ignore-start format: Keeps lines in 1 piece
|
||||||
globalScene.arena.eventTarget.addEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.WEATHER_CHANGED, this.onWeatherChangedEvent);
|
||||||
globalScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.TERRAIN_CHANGED, this.onTerrainChangedEvent);
|
||||||
globalScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.ARENA_TAG_ADDED, this.onArenaTagAddedEvent);
|
||||||
}
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.ARENA_TAG_REMOVED, this.onArenaTagRemovedEvent);
|
||||||
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.POSITIONAL_TAG_ADDED, this.onPositionalTagAddedEvent);
|
||||||
/** Clears out the current string stored in all arena effect texts */
|
globalScene.arena.eventTarget.addEventListener(ArenaEventType.POSITIONAL_TAG_REMOVED, this.onPositionalTagRemovedEvent);
|
||||||
private clearText() {
|
// biome-ignore-end format: Keeps lines in 1 piece
|
||||||
this.flyoutTextPlayer.text = "";
|
|
||||||
this.flyoutTextField.text = "";
|
|
||||||
this.flyoutTextEnemy.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parses through all set Arena Effects and puts them into the proper {@linkcode Phaser.GameObjects.Text} object */
|
|
||||||
private updateFieldText() {
|
|
||||||
this.clearText();
|
|
||||||
|
|
||||||
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.fieldEffectInfo.length; i++) {
|
|
||||||
const fieldEffectInfo = this.fieldEffectInfo[i];
|
|
||||||
|
|
||||||
// Creates a proxy object to decide which text object needs to be updated
|
|
||||||
let textObject: Phaser.GameObjects.Text;
|
|
||||||
switch (fieldEffectInfo.effecType) {
|
|
||||||
case ArenaEffectType.PLAYER:
|
|
||||||
textObject = this.flyoutTextPlayer;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArenaEffectType.WEATHER:
|
|
||||||
case ArenaEffectType.TERRAIN:
|
|
||||||
case ArenaEffectType.FIELD:
|
|
||||||
textObject = this.flyoutTextField;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArenaEffectType.ENEMY:
|
|
||||||
textObject = this.flyoutTextEnemy;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
textObject.text += fieldEffectInfo.name;
|
|
||||||
|
|
||||||
if (fieldEffectInfo.maxDuration !== 0) {
|
|
||||||
textObject.text += " " + fieldEffectInfo.duration + "/" + fieldEffectInfo.maxDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
textObject.text += "\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the {@linkcode Event} being passed and updates the state of the fieldEffectInfo array
|
* Iterate through all currently present field effects and decrement their durations.
|
||||||
* @param event {@linkcode Event} being sent
|
|
||||||
*/
|
*/
|
||||||
private onFieldEffectChanged(event: Event) {
|
private onTurnEnd() {
|
||||||
const arenaEffectChangedEvent = event as ArenaEvent;
|
// Remove all objects with positive max durations and whose durations have expired.
|
||||||
if (!arenaEffectChangedEvent) {
|
this.arenaTags = this.arenaTags.filter(info => info.maxDuration === 0 || --info.duration >= 0);
|
||||||
|
this.positionalTags = this.positionalTags.filter(info => --info.duration >= 0);
|
||||||
|
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region ArenaTags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a recently-created {@linkcode ArenaTag} to the flyout.
|
||||||
|
* @param event - The {@linkcode ArenaTagAddedEvent} having been emitted
|
||||||
|
*/
|
||||||
|
private onArenaTagAdded(event: ArenaTagAddedEvent): void {
|
||||||
|
const name = localizeEffectName(ArenaTagType[event.tagType]);
|
||||||
|
// Ternary used to avoid unneeded find
|
||||||
|
const existingTrapTag =
|
||||||
|
event.trapLayers !== undefined
|
||||||
|
? this.arenaTags.find(e => e.tagType === event.tagType && e.side === event.side)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
// If we got signalled for a layer count update, update the existing trap's name.
|
||||||
|
// Otherwise, push it to the array.
|
||||||
|
if (event.trapLayers !== undefined && existingTrapTag) {
|
||||||
|
this.updateTrapLayers(existingTrapTag, event.trapLayers, name);
|
||||||
|
} else {
|
||||||
|
this.arenaTags.push({
|
||||||
|
name,
|
||||||
|
side: event.side,
|
||||||
|
maxDuration: event.duration,
|
||||||
|
duration: event.duration,
|
||||||
|
tagType: event.tagType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing trap tag with an updated layer count whenever one is overlapped.
|
||||||
|
* @param existingTag - The existing {@linkcode ArenaTagInfo} to update text for
|
||||||
|
* @param layers - The base number of layers of the new tag
|
||||||
|
* @param maxLayers - The maximum number of layers of the new tag; will not show layer count if <=0
|
||||||
|
* @param name - The name of the tag.
|
||||||
|
*/
|
||||||
|
private updateTrapLayers(existingTag: ArenaTagInfo, [layers, maxLayers]: [number, number], name: string): void {
|
||||||
|
const layerStr = maxLayers > 1 ? ` (${layers})` : "";
|
||||||
|
existingTag.name = `${name}${layerStr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a recently-culled {@linkcode ArenaTag} from the flyout.
|
||||||
|
* @param event - The {@linkcode ArenaTagRemovedEvent} having been emitted
|
||||||
|
*/
|
||||||
|
private onArenaTagRemoved(event: ArenaTagRemovedEvent): void {
|
||||||
|
const foundIndex = this.arenaTags.findIndex(info => info.tagType === event.tagType && info.side === event.side);
|
||||||
|
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
// If the tag was being tracked, remove it
|
||||||
|
this.arenaTags.splice(foundIndex, 1);
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion ArenaTags
|
||||||
|
|
||||||
|
// #region PositionalTags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a recently-created {@linkcode PositionalTag} to the flyout.
|
||||||
|
* @param event - The {@linkcode PositionalTagAddedEvent} having been emitted
|
||||||
|
*/
|
||||||
|
private onPositionalTagAdded(event: PositionalTagAddedEvent): void {
|
||||||
|
const name = getPositionalTagDisplayName(event.tag);
|
||||||
|
|
||||||
|
this.positionalTags.push({
|
||||||
|
name,
|
||||||
|
targetIndex: event.tag.targetIndex,
|
||||||
|
duration: event.tag.turnCount,
|
||||||
|
tagType: event.tag.tagType,
|
||||||
|
});
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a recently-activated {@linkcode PositionalTag} from the flyout.
|
||||||
|
* @param event - The {@linkcode PositionalTagRemovedEvent} having been emitted
|
||||||
|
*/
|
||||||
|
private onPositionalTagRemoved(event: PositionalTagRemovedEvent): void {
|
||||||
|
const foundIndex = this.positionalTags.findIndex(
|
||||||
|
info => info.tagType === event.tagType && info.targetIndex === event.targetIndex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
// If the tag was being tracked, remove it
|
||||||
|
this.positionalTags.splice(foundIndex, 1);
|
||||||
|
this.updateFieldText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion PositionalTags
|
||||||
|
|
||||||
|
// #region Weather/Terrain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current weather text when the weather changes.
|
||||||
|
* @param event - The {@linkcode WeatherChangedEvent} having been emitted
|
||||||
|
*/
|
||||||
|
private onWeatherChanged(event: WeatherChangedEvent) {
|
||||||
|
// If weather was reset, clear the current data.
|
||||||
|
if (event.weatherType === WeatherType.NONE) {
|
||||||
|
this.weatherInfo = undefined;
|
||||||
|
this.updateFieldText();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let foundIndex: number;
|
this.weatherInfo = {
|
||||||
switch (arenaEffectChangedEvent.constructor) {
|
name: localizeEffectName(WeatherType[event.weatherType]),
|
||||||
case TagAddedEvent: {
|
maxDuration: event.duration,
|
||||||
const tagAddedEvent = arenaEffectChangedEvent as TagAddedEvent;
|
duration: event.duration,
|
||||||
const isArenaTrapTag = globalScene.arena.getTag(tagAddedEvent.arenaTagType) instanceof ArenaTrapTag;
|
weatherType: event.weatherType,
|
||||||
let arenaEffectType: ArenaEffectType;
|
};
|
||||||
|
|
||||||
if (tagAddedEvent.arenaTagSide === ArenaTagSide.BOTH) {
|
|
||||||
arenaEffectType = ArenaEffectType.FIELD;
|
|
||||||
} else if (tagAddedEvent.arenaTagSide === ArenaTagSide.PLAYER) {
|
|
||||||
arenaEffectType = ArenaEffectType.PLAYER;
|
|
||||||
} else {
|
|
||||||
arenaEffectType = ArenaEffectType.ENEMY;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingTrapTagIndex = isArenaTrapTag
|
|
||||||
? this.fieldEffectInfo.findIndex(
|
|
||||||
e => tagAddedEvent.arenaTagType === e.tagType && arenaEffectType === e.effecType,
|
|
||||||
)
|
|
||||||
: -1;
|
|
||||||
let name: string = getFieldEffectText(ArenaTagType[tagAddedEvent.arenaTagType]);
|
|
||||||
|
|
||||||
if (isArenaTrapTag) {
|
|
||||||
if (existingTrapTagIndex !== -1) {
|
|
||||||
const layers = tagAddedEvent.arenaTagMaxLayers > 1 ? ` (${tagAddedEvent.arenaTagLayers})` : "";
|
|
||||||
this.fieldEffectInfo[existingTrapTagIndex].name = `${name}${layers}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (tagAddedEvent.arenaTagMaxLayers > 1) {
|
|
||||||
name = `${name} (${tagAddedEvent.arenaTagLayers})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fieldEffectInfo.push({
|
|
||||||
name,
|
|
||||||
effecType: arenaEffectType,
|
|
||||||
maxDuration: tagAddedEvent.duration,
|
|
||||||
duration: tagAddedEvent.duration,
|
|
||||||
tagType: tagAddedEvent.arenaTagType,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TagRemovedEvent: {
|
|
||||||
const tagRemovedEvent = arenaEffectChangedEvent as TagRemovedEvent;
|
|
||||||
foundIndex = this.fieldEffectInfo.findIndex(info => info.tagType === tagRemovedEvent.arenaTagType);
|
|
||||||
|
|
||||||
if (foundIndex !== -1) {
|
|
||||||
// If the tag was being tracked, remove it
|
|
||||||
this.fieldEffectInfo.splice(foundIndex, 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case WeatherChangedEvent:
|
|
||||||
case TerrainChangedEvent: {
|
|
||||||
const fieldEffectChangedEvent = arenaEffectChangedEvent as WeatherChangedEvent | TerrainChangedEvent;
|
|
||||||
|
|
||||||
// Stores the old Weather/Terrain name in case it's in the array already
|
|
||||||
const oldName = getFieldEffectText(
|
|
||||||
fieldEffectChangedEvent instanceof WeatherChangedEvent
|
|
||||||
? WeatherType[fieldEffectChangedEvent.oldWeatherType]
|
|
||||||
: TerrainType[fieldEffectChangedEvent.oldTerrainType],
|
|
||||||
);
|
|
||||||
// Stores the new Weather/Terrain info
|
|
||||||
const newInfo = {
|
|
||||||
name: getFieldEffectText(
|
|
||||||
fieldEffectChangedEvent instanceof WeatherChangedEvent
|
|
||||||
? WeatherType[fieldEffectChangedEvent.newWeatherType]
|
|
||||||
: TerrainType[fieldEffectChangedEvent.newTerrainType],
|
|
||||||
),
|
|
||||||
effecType:
|
|
||||||
fieldEffectChangedEvent instanceof WeatherChangedEvent ? ArenaEffectType.WEATHER : ArenaEffectType.TERRAIN,
|
|
||||||
maxDuration: fieldEffectChangedEvent.duration,
|
|
||||||
duration: fieldEffectChangedEvent.duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
foundIndex = this.fieldEffectInfo.findIndex(info => [newInfo.name, oldName].includes(info.name));
|
|
||||||
if (foundIndex === -1) {
|
|
||||||
if (newInfo.name !== undefined) {
|
|
||||||
this.fieldEffectInfo.push(newInfo); // Adds the info to the array if it doesn't already exist and is defined
|
|
||||||
}
|
|
||||||
} else if (!newInfo.name) {
|
|
||||||
this.fieldEffectInfo.splice(foundIndex, 1); // Removes the old info if the new one is undefined
|
|
||||||
} else {
|
|
||||||
this.fieldEffectInfo[foundIndex] = newInfo; // Otherwise, replace the old info
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateFieldText();
|
this.updateFieldText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates through the fieldEffectInfo array and decrements the duration of each item
|
* Update the current terrain text when the terrain changes.
|
||||||
* @param event {@linkcode Event} being sent
|
* @param event - The {@linkcode TerrainChangedEvent} having been emitted
|
||||||
*/
|
*/
|
||||||
private onTurnEnd(event: Event) {
|
private onTerrainChanged(event: TerrainChangedEvent) {
|
||||||
const turnEndEvent = event as TurnEndEvent;
|
// If terrain was reset, clear the current data.
|
||||||
if (!turnEndEvent) {
|
if (event.terrainType === TerrainType.NONE) {
|
||||||
|
this.terrainInfo = undefined;
|
||||||
|
this.updateFieldText();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldEffectInfo: ArenaEffectInfo[] = [];
|
this.terrainInfo = {
|
||||||
this.fieldEffectInfo.forEach(i => fieldEffectInfo.push(i));
|
name: localizeEffectName(TerrainType[event.terrainType]),
|
||||||
|
maxDuration: event.duration,
|
||||||
for (let i = 0; i < fieldEffectInfo.length; i++) {
|
duration: event.duration,
|
||||||
const info = fieldEffectInfo[i];
|
terrainType: event.terrainType,
|
||||||
|
};
|
||||||
if (info.maxDuration === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
--info.duration;
|
|
||||||
if (info.duration <= 0) {
|
|
||||||
// Removes the item if the duration has expired
|
|
||||||
this.fieldEffectInfo.splice(this.fieldEffectInfo.indexOf(info), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateFieldText();
|
this.updateFieldText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #endregion Weather/Terrain
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animates the flyout to either show or hide it by applying a fade and translation
|
* Animate the flyout to either show or hide the modal.
|
||||||
* @param visible Should the flyout be shown?
|
* @param visible - Whether the the flyout should be shown
|
||||||
*/
|
*/
|
||||||
public toggleFlyout(visible: boolean): void {
|
public toggleFlyout(visible: boolean): void {
|
||||||
globalScene.tweens.add({
|
globalScene.tweens.add({
|
||||||
@ -421,15 +479,128 @@ export class ArenaFlyout extends Phaser.GameObjects.Container {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Destroy this element and remove all associated listeners. */
|
||||||
public destroy(fromScene?: boolean): void {
|
public destroy(fromScene?: boolean): void {
|
||||||
globalScene.eventTarget.removeEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
globalScene.eventTarget.removeEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
|
||||||
globalScene.eventTarget.removeEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
globalScene.eventTarget.removeEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
|
||||||
|
|
||||||
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.WEATHER_CHANGED, this.onWeatherChanged);
|
||||||
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.TERRAIN_CHANGED, this.onTerrainChanged);
|
||||||
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.ARENA_TAG_ADDED, this.onArenaTagAddedEvent);
|
||||||
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.ARENA_TAG_REMOVED, this.onArenaTagRemovedEvent);
|
||||||
|
// biome-ignore format: Keeps lines in 1 piece
|
||||||
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.POSITIONAL_TAG_ADDED, this.onPositionalTagAddedEvent);
|
||||||
|
// biome-ignore format: Keeps lines in 1 piece
|
||||||
|
globalScene.arena.eventTarget.removeEventListener(ArenaEventType.POSITIONAL_TAG_REMOVED, this.onPositionalTagRemovedEvent);
|
||||||
|
|
||||||
super.destroy(fromScene);
|
super.destroy(fromScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clear out the contents of all arena texts. */
|
||||||
|
private clearText() {
|
||||||
|
this.flyoutTextPlayer.text = "";
|
||||||
|
this.flyoutTextField.text = "";
|
||||||
|
this.flyoutTextEnemy.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Text display functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all field effects and update the corresponding {@linkcode Phaser.GameObjects.Text} object.
|
||||||
|
*/
|
||||||
|
private updateFieldText(): void {
|
||||||
|
this.clearText();
|
||||||
|
|
||||||
|
// Weather and terrain go first
|
||||||
|
if (this.weatherInfo) {
|
||||||
|
this.updateTagText(this.weatherInfo);
|
||||||
|
}
|
||||||
|
if (this.terrainInfo) {
|
||||||
|
this.updateTagText(this.terrainInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort and add all positional tags
|
||||||
|
this.positionalTags.sort(
|
||||||
|
// Sort based on tag name, breaking ties by ascending target index.
|
||||||
|
(infoA, infoB) => infoA.name.localeCompare(infoB.name) || infoA.targetIndex - infoB.targetIndex,
|
||||||
|
);
|
||||||
|
for (const tag of this.positionalTags) {
|
||||||
|
this.updatePosTagText(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort and update all arena tag text
|
||||||
|
this.arenaTags.sort((infoA, infoB) => infoA.duration - infoB.duration);
|
||||||
|
for (const tag of this.arenaTags) {
|
||||||
|
this.updateTagText(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to update the flyout box's text with a {@linkcode PositionalTag}'s info.
|
||||||
|
* @param info - The {@linkcode PositionalTagInfo} whose text is being updated
|
||||||
|
*/
|
||||||
|
private updatePosTagText(info: PositionalTagInfo): void {
|
||||||
|
const textObj = this.getPositionalTagTextObj(info);
|
||||||
|
|
||||||
|
const targetPos = battlerIndexToFieldPosition(info.targetIndex);
|
||||||
|
const posText = localizeEffectName(FieldPosition[targetPos]);
|
||||||
|
|
||||||
|
// Ex: "Future Sight (Center, 2)"
|
||||||
|
textObj.text += `${info.name} (${posText}, ${info.duration}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to update the flyout box's text with an effect's info.
|
||||||
|
* @param info - The {@linkcode ArenaTagInfo}, {@linkcode TerrainInfo} or {@linkcode WeatherInfo} being updated
|
||||||
|
*/
|
||||||
|
private updateTagText(info: ArenaTagInfo | WeatherInfo | TerrainInfo): void {
|
||||||
|
// Weathers and terrains use the "field" box by default
|
||||||
|
const textObject = "tagType" in info ? this.getArenaTagTargetObj(info.side) : this.flyoutTextField;
|
||||||
|
|
||||||
|
textObject.text += info.name;
|
||||||
|
|
||||||
|
if (info.maxDuration > 0) {
|
||||||
|
textObject.text += ` ${info.duration}/ + ${info.maxDuration}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
textObject.text += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to select the text object needing to be updated depending on the current tag's side.
|
||||||
|
* @param side - The {@linkcode ArenaTagSide} of the tag being updated
|
||||||
|
* @returns The {@linkcode Phaser.GameObjects.Text} to be updated.
|
||||||
|
*/
|
||||||
|
private getArenaTagTargetObj(side: ArenaTagSide): Phaser.GameObjects.Text {
|
||||||
|
switch (side) {
|
||||||
|
case ArenaTagSide.PLAYER:
|
||||||
|
return this.flyoutTextPlayer;
|
||||||
|
case ArenaTagSide.ENEMY:
|
||||||
|
return this.flyoutTextEnemy;
|
||||||
|
case ArenaTagSide.BOTH:
|
||||||
|
return this.flyoutTextField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose which text object needs to be updated depending on the current tag's target.
|
||||||
|
* @param info - The {@linkcode PositionalTagInfo} being displayed
|
||||||
|
* @returns The {@linkcode Phaser.GameObjects.Text} to be updated.
|
||||||
|
*/
|
||||||
|
private getPositionalTagTextObj(info: PositionalTagInfo): Phaser.GameObjects.Text {
|
||||||
|
switch (info.targetIndex) {
|
||||||
|
case BattlerIndex.PLAYER:
|
||||||
|
case BattlerIndex.PLAYER_2:
|
||||||
|
return this.flyoutTextPlayer;
|
||||||
|
case BattlerIndex.ENEMY:
|
||||||
|
case BattlerIndex.ENEMY_2:
|
||||||
|
return this.flyoutTextEnemy;
|
||||||
|
case BattlerIndex.ATTACKER:
|
||||||
|
throw new Error("BattlerIndex.ATTACKER used as tag target index for arena flyout!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private;
|
||||||
|
|
||||||
|
// # endregion Text display functions
|
||||||
}
|
}
|
||||||
|
124
src/ui/change-password-form-ui-handler.ts
Normal file
124
src/ui/change-password-form-ui-handler.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
|
import { UiMode } from "#enums/ui-mode";
|
||||||
|
import type { InputFieldConfig } from "#ui/form-modal-ui-handler";
|
||||||
|
import { FormModalUiHandler } from "#ui/form-modal-ui-handler";
|
||||||
|
import type { ModalConfig } from "#ui/modal-ui-handler";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
export class ChangePasswordFormUiHandler extends FormModalUiHandler {
|
||||||
|
private readonly ERR_PASSWORD: string = "invalid password";
|
||||||
|
private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist";
|
||||||
|
private readonly ERR_PASSWORD_MISMATCH: string = "password doesn't match";
|
||||||
|
|
||||||
|
constructor(mode: UiMode | null = null) {
|
||||||
|
super(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(): void {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override getModalTitle(_config?: ModalConfig): string {
|
||||||
|
return i18next.t("menu:changePassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
override getWidth(_config?: ModalConfig): number {
|
||||||
|
return 160;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getMargin(_config?: ModalConfig): [number, number, number, number] {
|
||||||
|
return [0, 0, 48, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
override getButtonLabels(_config?: ModalConfig): string[] {
|
||||||
|
return [i18next.t("settings:buttonSubmit"), i18next.t("menu:cancel")];
|
||||||
|
}
|
||||||
|
|
||||||
|
override getReadableErrorMessage(error: string): string {
|
||||||
|
const colonIndex = error?.indexOf(":");
|
||||||
|
if (colonIndex > 0) {
|
||||||
|
error = error.slice(0, colonIndex);
|
||||||
|
}
|
||||||
|
switch (error) {
|
||||||
|
case this.ERR_PASSWORD:
|
||||||
|
return i18next.t("menu:invalidRegisterPassword");
|
||||||
|
case this.ERR_ACCOUNT_EXIST:
|
||||||
|
return i18next.t("menu:accountNonExistent");
|
||||||
|
case this.ERR_PASSWORD_MISMATCH:
|
||||||
|
return i18next.t("menu:passwordNotMatchingConfirmPassword");
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getReadableErrorMessage(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getInputFieldConfigs(): InputFieldConfig[] {
|
||||||
|
const inputFieldConfigs: InputFieldConfig[] = [];
|
||||||
|
inputFieldConfigs.push({
|
||||||
|
label: i18next.t("menu:password"),
|
||||||
|
isPassword: true,
|
||||||
|
});
|
||||||
|
inputFieldConfigs.push({
|
||||||
|
label: i18next.t("menu:confirmPassword"),
|
||||||
|
isPassword: true,
|
||||||
|
});
|
||||||
|
return inputFieldConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
override show(args: [ModalConfig, ...any]): boolean {
|
||||||
|
if (super.show(args)) {
|
||||||
|
const config = args[0];
|
||||||
|
const originalSubmitAction = this.submitAction;
|
||||||
|
this.submitAction = () => {
|
||||||
|
if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) {
|
||||||
|
// Prevent overlapping overrides on action modification
|
||||||
|
this.submitAction = originalSubmitAction;
|
||||||
|
this.sanitizeInputs();
|
||||||
|
globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] });
|
||||||
|
const onFail = (error: string | null) => {
|
||||||
|
globalScene.ui.setMode(UiMode.CHANGE_PASSWORD_FORM, Object.assign(config, { errorMessage: error?.trim() }));
|
||||||
|
globalScene.ui.playError();
|
||||||
|
};
|
||||||
|
const [passwordInput, confirmPasswordInput] = this.inputs;
|
||||||
|
if (!passwordInput?.text) {
|
||||||
|
return onFail(this.getReadableErrorMessage("invalid password"));
|
||||||
|
}
|
||||||
|
if (passwordInput.text !== confirmPasswordInput.text) {
|
||||||
|
return onFail(this.ERR_PASSWORD_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
pokerogueApi.account.changePassword({ password: passwordInput.text }).then(error => {
|
||||||
|
if (!error && originalSubmitAction) {
|
||||||
|
globalScene.ui.playSelect();
|
||||||
|
originalSubmitAction();
|
||||||
|
// Only clear inputs if the action was successful
|
||||||
|
for (const input of this.inputs) {
|
||||||
|
input.setText("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onFail(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Upon pressing cancel, the inputs should be cleared
|
||||||
|
const originalCancelAction = this.cancelAction;
|
||||||
|
this.cancelAction = () => {
|
||||||
|
globalScene.ui.playSelect();
|
||||||
|
for (const input of this.inputs) {
|
||||||
|
input.setText("");
|
||||||
|
}
|
||||||
|
originalCancelAction?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override clear() {
|
||||||
|
super.clear();
|
||||||
|
this.setMouseCursorStyle("default"); //reset cursor
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
|
|||||||
protected inputs: InputText[];
|
protected inputs: InputText[];
|
||||||
protected errorMessage: Phaser.GameObjects.Text;
|
protected errorMessage: Phaser.GameObjects.Text;
|
||||||
protected submitAction: Function | null;
|
protected submitAction: Function | null;
|
||||||
|
protected cancelAction: (() => void) | null;
|
||||||
protected tween: Phaser.Tweens.Tween;
|
protected tween: Phaser.Tweens.Tween;
|
||||||
protected formLabels: Phaser.GameObjects.Text[];
|
protected formLabels: Phaser.GameObjects.Text[];
|
||||||
|
|
||||||
@ -126,22 +127,37 @@ export abstract class FormModalUiHandler extends ModalUiHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
show(args: any[]): boolean {
|
override show(args: any[]): boolean {
|
||||||
if (super.show(args)) {
|
if (super.show(args)) {
|
||||||
this.inputContainers.map(ic => ic.setVisible(true));
|
this.inputContainers.map(ic => ic.setVisible(true));
|
||||||
|
|
||||||
const config = args[0] as FormModalConfig;
|
const config = args[0] as FormModalConfig;
|
||||||
|
|
||||||
this.submitAction = config.buttonActions.length ? config.buttonActions[0] : null;
|
this.submitAction = config.buttonActions.length ? config.buttonActions[0] : null;
|
||||||
|
this.cancelAction = config.buttonActions[1] ?? null;
|
||||||
|
|
||||||
if (this.buttonBgs.length) {
|
// #region: Override button pointerDown
|
||||||
this.buttonBgs[0].off("pointerdown");
|
// Override the pointerDown event for the buttonBgs to call the `submitAction` and `cancelAction`
|
||||||
this.buttonBgs[0].on("pointerdown", () => {
|
// properties that we set above, allowing their behavior to change after this method terminates
|
||||||
if (this.submitAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) {
|
// Some subclasses use this to add behavior to the submit and cancel action
|
||||||
this.submitAction();
|
|
||||||
|
this.buttonBgs[0].off("pointerdown");
|
||||||
|
this.buttonBgs[0].on("pointerdown", () => {
|
||||||
|
if (this.submitAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) {
|
||||||
|
this.submitAction();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cancelBg = this.buttonBgs[1];
|
||||||
|
if (cancelBg) {
|
||||||
|
cancelBg.off("pointerdown");
|
||||||
|
cancelBg.on("pointerdown", () => {
|
||||||
|
// The seemingly redundant cancelAction check is intentionally left in as a defensive programming measure
|
||||||
|
if (this.cancelAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) {
|
||||||
|
this.cancelAction();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//#endregion: Override pointerDown events
|
||||||
|
|
||||||
this.modalContainer.y += 24;
|
this.modalContainer.y += 24;
|
||||||
this.modalContainer.setAlpha(0);
|
this.modalContainer.setAlpha(0);
|
||||||
|
@ -311,6 +311,17 @@ export class MenuUiHandler extends MessageUiHandler {
|
|||||||
},
|
},
|
||||||
keepOpen: true,
|
keepOpen: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Note: i18n key is under `menu`, not `menuUiHandler` to avoid duplication
|
||||||
|
label: i18next.t("menu:changePassword"),
|
||||||
|
handler: () => {
|
||||||
|
ui.setOverlayMode(UiMode.CHANGE_PASSWORD_FORM, {
|
||||||
|
buttonActions: [() => ui.revertMode(), () => ui.revertMode()],
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keepOpen: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: i18next.t("menuUiHandler:consentPreferences"),
|
label: i18next.t("menuUiHandler:consentPreferences"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
|
@ -7,7 +7,7 @@ import { UiHandler } from "#ui/ui-handler";
|
|||||||
import { addWindow, WindowVariant } from "#ui/ui-theme";
|
import { addWindow, WindowVariant } from "#ui/ui-theme";
|
||||||
|
|
||||||
export interface ModalConfig {
|
export interface ModalConfig {
|
||||||
buttonActions: Function[];
|
buttonActions: ((...args: any[]) => any)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ModalUiHandler extends UiHandler {
|
export abstract class ModalUiHandler extends UiHandler {
|
||||||
|
@ -13,6 +13,7 @@ import { BallUiHandler } from "#ui/ball-ui-handler";
|
|||||||
import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
|
import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
|
||||||
import type { BgmBar } from "#ui/bgm-bar";
|
import type { BgmBar } from "#ui/bgm-bar";
|
||||||
import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler";
|
import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler";
|
||||||
|
import { ChangePasswordFormUiHandler } from "#ui/change-password-form-ui-handler";
|
||||||
import { CommandUiHandler } from "#ui/command-ui-handler";
|
import { CommandUiHandler } from "#ui/command-ui-handler";
|
||||||
import { ConfirmUiHandler } from "#ui/confirm-ui-handler";
|
import { ConfirmUiHandler } from "#ui/confirm-ui-handler";
|
||||||
import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler";
|
import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler";
|
||||||
@ -102,6 +103,7 @@ const noTransitionModes = [
|
|||||||
UiMode.ADMIN,
|
UiMode.ADMIN,
|
||||||
UiMode.MYSTERY_ENCOUNTER,
|
UiMode.MYSTERY_ENCOUNTER,
|
||||||
UiMode.RUN_INFO,
|
UiMode.RUN_INFO,
|
||||||
|
UiMode.CHANGE_PASSWORD_FORM,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class UI extends Phaser.GameObjects.Container {
|
export class UI extends Phaser.GameObjects.Container {
|
||||||
@ -172,6 +174,7 @@ export class UI extends Phaser.GameObjects.Container {
|
|||||||
new AutoCompleteUiHandler(),
|
new AutoCompleteUiHandler(),
|
||||||
new AdminUiHandler(),
|
new AdminUiHandler(),
|
||||||
new MysteryEncounterUiHandler(),
|
new MysteryEncounterUiHandler(),
|
||||||
|
new ChangePasswordFormUiHandler(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user