Merge branch 'beta' into pr-illusion
This commit is contained in:
commit
5acf933066
|
@ -10,6 +10,7 @@
|
|||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"compare-versions": "^6.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-browser-languagedetector": "^8.0.4",
|
||||
|
@ -3605,6 +3606,12 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/compare-versions": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz",
|
||||
"integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"compare-versions": "^6.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-browser-languagedetector": "^8.0.4",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import type { SessionSaveData } from "#app/system/game-data";
|
||||
|
||||
export interface SessionSaveMigrator {
|
||||
version: string;
|
||||
migrate: (data: SessionSaveData) => void;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface SettingsSaveMigrator {
|
||||
version: string;
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
|
||||
migrate: (data: Object) => void;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import type { SystemSaveData } from "#app/system/game-data";
|
||||
|
||||
export interface SystemSaveMigrator {
|
||||
version: string;
|
||||
migrate: (data: SystemSaveData) => void;
|
||||
}
|
|
@ -2295,6 +2295,11 @@ export class PostSummonAbAttr extends AbAttr {
|
|||
applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for ability attributes which remove an effect on summon
|
||||
*/
|
||||
export class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {}
|
||||
|
||||
/**
|
||||
* Removes specified arena tags when a Pokemon is summoned.
|
||||
*/
|
||||
|
@ -2417,6 +2422,31 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes Specific battler tags when a Pokemon is summoned
|
||||
*
|
||||
* This should realistically only ever activate on gain rather than on summon
|
||||
*/
|
||||
export class PostSummonRemoveBattlerTagAbAttr extends PostSummonRemoveEffectAbAttr {
|
||||
private immuneTags: BattlerTagType[];
|
||||
|
||||
/**
|
||||
* @param immuneTags - The {@linkcode BattlerTagType | battler tags} the Pokémon is immune to.
|
||||
*/
|
||||
constructor(...immuneTags: BattlerTagType[]) {
|
||||
super();
|
||||
this.immuneTags = immuneTags;
|
||||
}
|
||||
|
||||
public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||
return this.immuneTags.some(tagType => !!pokemon.getTag(tagType));
|
||||
}
|
||||
|
||||
public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||
this.immuneTags.forEach(tagType => pokemon.removeTag(tagType));
|
||||
}
|
||||
}
|
||||
|
||||
export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr {
|
||||
private stats: BattleStat[];
|
||||
private stages: number;
|
||||
|
@ -2604,6 +2634,43 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Heals a status effect if the Pokemon is afflicted with it upon switch in (or gain)
|
||||
*/
|
||||
export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr {
|
||||
private immuneEffects: StatusEffect[];
|
||||
private statusHealed: StatusEffect;
|
||||
|
||||
/**
|
||||
* @param immuneEffects - The {@linkcode StatusEffect}s the Pokémon is immune to.
|
||||
*/
|
||||
constructor(...immuneEffects: StatusEffect[]) {
|
||||
super();
|
||||
this.immuneEffects = immuneEffects;
|
||||
}
|
||||
|
||||
public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||
const status = pokemon.status?.effect;
|
||||
return !Utils.isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status))
|
||||
}
|
||||
|
||||
public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||
const status = pokemon.status?.effect;
|
||||
if (!Utils.isNullOrUndefined(status)) {
|
||||
this.statusHealed = status;
|
||||
pokemon.resetStatus(false);
|
||||
pokemon.updateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public override getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null {
|
||||
if (this.statusHealed) {
|
||||
return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(_pokemon));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostSummonFormChangeAbAttr extends PostSummonAbAttr {
|
||||
private formFunc: (p: Pokemon) => number;
|
||||
|
||||
|
@ -6410,6 +6477,7 @@ export function initAbilities() {
|
|||
.ignorable(),
|
||||
new Ability(Abilities.LIMBER, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.PARALYSIS)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.PARALYSIS)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SAND_VEIL, 3)
|
||||
.attr(StatMultiplierAbAttr, Stat.EVA, 1.2)
|
||||
|
@ -6427,6 +6495,7 @@ export function initAbilities() {
|
|||
.ignorable(),
|
||||
new Ability(Abilities.OBLIVIOUS, 3)
|
||||
.attr(BattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT ])
|
||||
.attr(PostSummonRemoveBattlerTagAbAttr, BattlerTagType.INFATUATED, BattlerTagType.TAUNT)
|
||||
.attr(IntimidateImmunityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.CLOUD_NINE, 3)
|
||||
|
@ -6439,6 +6508,7 @@ export function initAbilities() {
|
|||
.attr(StatMultiplierAbAttr, Stat.ACC, 1.3),
|
||||
new Ability(Abilities.INSOMNIA, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.SLEEP)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.COLOR_CHANGE, 3)
|
||||
|
@ -6446,6 +6516,7 @@ export function initAbilities() {
|
|||
.condition(getSheerForceHitDisableAbCondition()),
|
||||
new Ability(Abilities.IMMUNITY, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.FLASH_FIRE, 3)
|
||||
.attr(TypeImmunityAddBattlerTagAbAttr, PokemonType.FIRE, BattlerTagType.FIRE_BOOST, 1)
|
||||
|
@ -6455,6 +6526,7 @@ export function initAbilities() {
|
|||
.ignorable(),
|
||||
new Ability(Abilities.OWN_TEMPO, 3)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
|
||||
.attr(PostSummonRemoveBattlerTagAbAttr, BattlerTagType.CONFUSED)
|
||||
.attr(IntimidateImmunityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SUCTION_CUPS, 3)
|
||||
|
@ -6520,9 +6592,11 @@ export function initAbilities() {
|
|||
.ignorable(),
|
||||
new Ability(Abilities.MAGMA_ARMOR, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.FREEZE)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.FREEZE)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.WATER_VEIL, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.BURN)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.MAGNET_PULL, 3)
|
||||
.attr(ArenaTrapAbAttr, (user, target) => {
|
||||
|
@ -6616,6 +6690,7 @@ export function initAbilities() {
|
|||
.attr(DoubleBattleChanceAbAttr),
|
||||
new Ability(Abilities.VITAL_SPIRIT, 3)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.SLEEP)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.WHITE_SMOKE, 3)
|
||||
|
@ -6960,6 +7035,7 @@ export function initAbilities() {
|
|||
.attr(MoveTypeChangeAbAttr, PokemonType.ICE, 1.2, (user, target, move) => move.type === PokemonType.NORMAL && !move.hasAttr(VariableMoveTypeAttr)),
|
||||
new Ability(Abilities.SWEET_VEIL, 6)
|
||||
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.SLEEP)
|
||||
.attr(UserFieldBattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
|
||||
.ignorable()
|
||||
.partial(), // Mold Breaker ally should not be affected by Sweet Veil
|
||||
|
@ -7044,6 +7120,7 @@ export function initAbilities() {
|
|||
.attr(ReceivedTypeDamageMultiplierAbAttr, PokemonType.FIRE, 0.5)
|
||||
.attr(MoveTypePowerBoostAbAttr, PokemonType.WATER, 2)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.BURN)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.STEELWORKER, 7)
|
||||
.attr(MoveTypePowerBoostAbAttr, PokemonType.STEEL),
|
||||
|
@ -7324,6 +7401,7 @@ export function initAbilities() {
|
|||
new Ability(Abilities.THERMAL_EXCHANGE, 9)
|
||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
|
||||
.attr(PostSummonHealStatusAbAttr, StatusEffect.BURN)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.ANGER_SHELL, 9)
|
||||
.attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 1)
|
||||
|
|
|
@ -2443,12 +2443,8 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
|||
const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance;
|
||||
if (statusCheck) {
|
||||
const pokemon = this.selfTarget ? user : target;
|
||||
if (pokemon.status) {
|
||||
if (this.overrideStatus) {
|
||||
pokemon.resetStatus();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (pokemon.status && !this.overrideStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user !== target && target.isSafeguarded(user)) {
|
||||
|
@ -2457,8 +2453,8 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining)) {
|
||||
if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus)) {
|
||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ import { StatusEffect } from "#enums/status-effect";
|
|||
import { doShinySparkleAnim } from "#app/field/anims";
|
||||
import { MoveFlags } from "#enums/MoveFlags";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||
|
||||
export enum LearnMoveSituation {
|
||||
MISC,
|
||||
|
@ -5003,7 +5004,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
if (newTag.canAdd(this)) {
|
||||
this.summonData.tags.push(newTag);
|
||||
newTag.onAdd(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5674,8 +5674,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
sourcePokemon: Pokemon | null = null,
|
||||
turnsRemaining = 0,
|
||||
sourceText: string | null = null,
|
||||
overrideStatus?: boolean
|
||||
): boolean {
|
||||
if (!this.canSetStatus(effect, asPhase, false, sourcePokemon)) {
|
||||
if (!this.canSetStatus(effect, asPhase, overrideStatus, sourcePokemon)) {
|
||||
return false;
|
||||
}
|
||||
if (this.isFainted() && effect !== StatusEffect.FAINT) {
|
||||
|
@ -5691,6 +5692,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
if (asPhase) {
|
||||
if (overrideStatus) {
|
||||
this.resetStatus(false);
|
||||
}
|
||||
globalScene.unshiftPhase(
|
||||
new ObtainStatusEffectPhase(
|
||||
this.getBattlerIndex(),
|
||||
|
@ -5730,20 +5734,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call
|
||||
this.status = new Status(effect, 0, sleepTurnsRemaining?.value);
|
||||
|
||||
if (effect !== StatusEffect.FAINT) {
|
||||
globalScene.triggerPokemonFormChange(
|
||||
this,
|
||||
SpeciesFormChangeStatusEffectTrigger,
|
||||
true,
|
||||
);
|
||||
applyPostSetStatusAbAttrs(
|
||||
PostSetStatusAbAttr,
|
||||
this,
|
||||
effect,
|
||||
sourcePokemon,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5758,21 +5748,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
if (!revive && lastStatus === StatusEffect.FAINT) {
|
||||
return;
|
||||
}
|
||||
this.status = null;
|
||||
if (lastStatus === StatusEffect.SLEEP) {
|
||||
this.setFrameRate(10);
|
||||
if (this.getTag(BattlerTagType.NIGHTMARE)) {
|
||||
this.lapseTag(BattlerTagType.NIGHTMARE);
|
||||
}
|
||||
}
|
||||
if (confusion) {
|
||||
if (this.getTag(BattlerTagType.CONFUSED)) {
|
||||
this.lapseTag(BattlerTagType.CONFUSED);
|
||||
}
|
||||
}
|
||||
if (reloadAssets) {
|
||||
this.loadAssets(false).then(() => this.playAnim());
|
||||
}
|
||||
globalScene.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,6 +73,13 @@ export class BattleEndPhase extends BattlePhase {
|
|||
}
|
||||
|
||||
globalScene.clearEnemyHeldItemModifiers();
|
||||
for (const p of globalScene.getEnemyParty()) {
|
||||
try {
|
||||
p.destroy();
|
||||
} catch {
|
||||
console.warn("Unable to destroy stale pokemon object in BattleEndPhase:", p);
|
||||
}
|
||||
}
|
||||
|
||||
const lapsingModifiers = globalScene.findModifiers(
|
||||
m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier,
|
||||
|
|
|
@ -2,11 +2,18 @@ import { globalScene } from "#app/global-scene";
|
|||
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/ability";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
|
||||
export class MoveEndPhase extends PokemonPhase {
|
||||
private wasFollowUp: boolean;
|
||||
constructor(battlerIndex: BattlerIndex, wasFollowUp = false) {
|
||||
|
||||
/** Targets from the preceding MovePhase */
|
||||
private targets: Pokemon[];
|
||||
constructor(battlerIndex: BattlerIndex, targets: Pokemon[], wasFollowUp = false) {
|
||||
super(battlerIndex);
|
||||
|
||||
this.targets = targets;
|
||||
this.wasFollowUp = wasFollowUp;
|
||||
}
|
||||
|
||||
|
@ -17,9 +24,15 @@ export class MoveEndPhase extends PokemonPhase {
|
|||
if (!this.wasFollowUp && pokemon?.isActive(true)) {
|
||||
pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||
}
|
||||
|
||||
globalScene.arena.setIgnoreAbilities(false);
|
||||
|
||||
// Remove effects which were set on a Pokemon which removes them on summon (i.e. via Mold Breaker)
|
||||
for (const target of this.targets) {
|
||||
if (target) {
|
||||
applyPostSummonAbAttrs(PostSummonRemoveEffectAbAttr, target);
|
||||
}
|
||||
}
|
||||
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,11 @@ export class MovePhase extends BattlePhase {
|
|||
|
||||
// Check move to see if arena.ignoreAbilities should be true.
|
||||
if (!this.followUp || this.reflected) {
|
||||
if (this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })) {
|
||||
if (
|
||||
this.move
|
||||
.getMove()
|
||||
.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })
|
||||
) {
|
||||
globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +477,9 @@ export class MovePhase extends BattlePhase {
|
|||
* Queues a {@linkcode MoveEndPhase} and then ends the phase
|
||||
*/
|
||||
public end(): void {
|
||||
globalScene.unshiftPhase(new MoveEndPhase(this.pokemon.getBattlerIndex(), this.followUp));
|
||||
globalScene.unshiftPhase(
|
||||
new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), this.followUp),
|
||||
);
|
||||
|
||||
super.end();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import { StatusEffect } from "#app/enums/status-effect";
|
|||
import type Pokemon from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms";
|
||||
import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/ability";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
|
||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
private statusEffect?: StatusEffect;
|
||||
|
@ -44,6 +47,12 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||
this.sourceText ?? undefined,
|
||||
),
|
||||
);
|
||||
if (!isNullOrUndefined(this.statusEffect) && this.statusEffect !== StatusEffect.FAINT) {
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true);
|
||||
// If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards
|
||||
globalScene.arena.setIgnoreAbilities(false);
|
||||
applyPostSetStatusAbAttrs(PostSetStatusAbAttr, pokemon, this.statusEffect, this.sourcePokemon);
|
||||
}
|
||||
this.end();
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import type Pokemon from "#app/field/pokemon";
|
||||
import { BattlePhase } from "#app/phases/battle-phase";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
|
||||
/**
|
||||
* Phase which handles resetting a Pokemon's status to none
|
||||
*
|
||||
* This is necessary to perform in a phase primarly to ensure that the status icon disappears at the correct time in the battle
|
||||
*/
|
||||
export class ResetStatusPhase extends BattlePhase {
|
||||
private readonly pokemon: Pokemon;
|
||||
private readonly affectConfusion: boolean;
|
||||
private readonly reloadAssets: boolean;
|
||||
|
||||
constructor(pokemon: Pokemon, affectConfusion: boolean, reloadAssets: boolean) {
|
||||
super();
|
||||
|
||||
this.pokemon = pokemon;
|
||||
this.affectConfusion = affectConfusion;
|
||||
this.reloadAssets = reloadAssets;
|
||||
}
|
||||
|
||||
public override start() {
|
||||
const lastStatus = this.pokemon.status?.effect;
|
||||
this.pokemon.status = null;
|
||||
if (lastStatus === StatusEffect.SLEEP) {
|
||||
this.pokemon.setFrameRate(10);
|
||||
if (this.pokemon.getTag(BattlerTagType.NIGHTMARE)) {
|
||||
this.pokemon.lapseTag(BattlerTagType.NIGHTMARE);
|
||||
}
|
||||
}
|
||||
if (this.affectConfusion) {
|
||||
if (this.pokemon.getTag(BattlerTagType.CONFUSED)) {
|
||||
this.pokemon.lapseTag(BattlerTagType.CONFUSED);
|
||||
}
|
||||
}
|
||||
if (this.reloadAssets) {
|
||||
this.pokemon.loadAssets(false).then(() => this.pokemon.playAnim());
|
||||
}
|
||||
this.pokemon.updateInfo(true);
|
||||
this.end();
|
||||
}
|
||||
}
|
|
@ -1,19 +1,104 @@
|
|||
import type { SessionSaveData, SystemSaveData } from "../game-data";
|
||||
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
|
||||
import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator";
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import type { SessionSaveData, SystemSaveData } from "#app/system/game-data";
|
||||
import { compareVersions } from "compare-versions";
|
||||
import { version } from "../../../package.json";
|
||||
|
||||
/*
|
||||
// template for save migrator creation
|
||||
// versions/vA_B_C.ts
|
||||
|
||||
// The version for each migrator should match the filename, ie: `vA_B_C.ts` -> `version: "A.B.C"
|
||||
// This is the target version (aka the version we're ending up on after the migrators are run)
|
||||
|
||||
// The name for each migrator should match its purpose. For example, if you're fixing
|
||||
// the ability index of a pokemon, it might be called `migratePokemonAbilityIndex`
|
||||
|
||||
const systemMigratorA: SystemSaveMigrator = {
|
||||
version: "A.B.C",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
// migration code goes here
|
||||
},
|
||||
};
|
||||
|
||||
export const systemMigrators: Readonly<SystemSaveMigrator[]> = [systemMigratorA] as const;
|
||||
|
||||
const sessionMigratorA: SessionSaveMigrator = {
|
||||
version: "A.B.C",
|
||||
migrate: (data: SessionSaveData): void => {
|
||||
// migration code goes here
|
||||
},
|
||||
};
|
||||
|
||||
export const sessionMigrators: Readonly<SessionSaveMigrator[]> = [sessionMigratorA] as const;
|
||||
|
||||
const settingsMigratorA: SettingsSaveMigrator = {
|
||||
version: "A.B.C",
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
|
||||
migrate: (data: Object): void => {
|
||||
// migration code goes here
|
||||
},
|
||||
};
|
||||
|
||||
export const settingsMigrators: Readonly<SettingsSaveMigrator[]> = [settingsMigratorA] as const;
|
||||
*/
|
||||
|
||||
// --- vA.B.C PATCHES --- //
|
||||
// import * as vA_B_C from "./versions/vA_B_C";
|
||||
|
||||
// --- v1.0.4 (and below) PATCHES --- //
|
||||
import * as v1_0_4 from "./versions/v1_0_4";
|
||||
|
||||
// --- v1.1.0 PATCHES --- //
|
||||
import * as v1_1_0 from "./versions/v1_1_0";
|
||||
|
||||
// --- v1.7.0 PATCHES --- //
|
||||
import * as v1_7_0 from "./versions/v1_7_0";
|
||||
|
||||
// --- v1.8.3 PATCHES --- //
|
||||
import * as v1_8_3 from "./versions/v1_8_3";
|
||||
|
||||
const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value));
|
||||
/** Current game version */
|
||||
const LATEST_VERSION = version;
|
||||
|
||||
type SaveMigrator = SystemSaveMigrator | SessionSaveMigrator | SettingsSaveMigrator;
|
||||
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
|
||||
type SaveData = SystemSaveData | SessionSaveData | Object;
|
||||
|
||||
// To add a new set of migrators, create a new `.push()` line like so:
|
||||
// `systemMigrators.push(...vA_B_C.systemMigrators);`
|
||||
|
||||
/** All system save migrators */
|
||||
const systemMigrators: SystemSaveMigrator[] = [];
|
||||
systemMigrators.push(...v1_0_4.systemMigrators);
|
||||
systemMigrators.push(...v1_7_0.systemMigrators);
|
||||
systemMigrators.push(...v1_8_3.systemMigrators);
|
||||
|
||||
/** All session save migrators */
|
||||
const sessionMigrators: SessionSaveMigrator[] = [];
|
||||
sessionMigrators.push(...v1_0_4.sessionMigrators);
|
||||
sessionMigrators.push(...v1_7_0.sessionMigrators);
|
||||
|
||||
/** All settings migrators */
|
||||
const settingsMigrators: SettingsSaveMigrator[] = [];
|
||||
settingsMigrators.push(...v1_0_4.settingsMigrators);
|
||||
|
||||
/** Sorts migrators by their stated version, ensuring they are applied in order from oldest to newest */
|
||||
const sortMigrators = (migrators: SaveMigrator[]): void => {
|
||||
migrators.sort((a, b) => compareVersions(a.version, b.version));
|
||||
};
|
||||
|
||||
sortMigrators(systemMigrators);
|
||||
sortMigrators(sessionMigrators);
|
||||
sortMigrators(settingsMigrators);
|
||||
|
||||
const applyMigrators = (migrators: readonly SaveMigrator[], data: SaveData, saveVersion: string) => {
|
||||
for (const migrator of migrators) {
|
||||
const isMigratorVersionHigher = compareVersions(saveVersion, migrator.version) === -1;
|
||||
if (isMigratorVersionHigher) {
|
||||
migrator.migrate(data as any);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts incoming {@linkcode SystemSaveData} that has a version below the
|
||||
|
@ -26,12 +111,12 @@ const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value));
|
|||
* @see {@link SystemVersionConverter}
|
||||
*/
|
||||
export function applySystemVersionMigration(data: SystemSaveData) {
|
||||
const curVersion = data.gameVersion.split(".").map(value => Number.parseInt(value));
|
||||
const prevVersion = data.gameVersion;
|
||||
const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1;
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SystemVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
if (isCurrentVersionHigher) {
|
||||
applyMigrators(systemMigrators, data, prevVersion);
|
||||
console.log(`System data successfully migrated to v${LATEST_VERSION}!`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,12 +131,15 @@ export function applySystemVersionMigration(data: SystemSaveData) {
|
|||
* @see {@link SessionVersionConverter}
|
||||
*/
|
||||
export function applySessionVersionMigration(data: SessionSaveData) {
|
||||
const curVersion = data.gameVersion.split(".").map(value => Number.parseInt(value));
|
||||
const prevVersion = data.gameVersion;
|
||||
const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1;
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SessionVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
if (isCurrentVersionHigher) {
|
||||
// Always sanitize money as a safeguard
|
||||
data.money = Math.floor(data.money);
|
||||
|
||||
applyMigrators(sessionMigrators, data, prevVersion);
|
||||
console.log(`Session data successfully migrated to v${LATEST_VERSION}!`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,156 +153,13 @@ export function applySessionVersionMigration(data: SessionSaveData) {
|
|||
* @param data Settings data object
|
||||
* @see {@link SettingsVersionConverter}
|
||||
*/
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
|
||||
export function applySettingsVersionMigration(data: Object) {
|
||||
const gameVersion: string = data.hasOwnProperty("gameVersion") ? data["gameVersion"] : "1.0.0";
|
||||
const curVersion = gameVersion.split(".").map(value => Number.parseInt(value));
|
||||
const prevVersion: string = data.hasOwnProperty("gameVersion") ? data["gameVersion"] : "1.0.0";
|
||||
const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1;
|
||||
|
||||
if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) {
|
||||
const converter = new SettingsVersionConverter();
|
||||
converter.applyStaticPreprocessors(data);
|
||||
converter.applyMigration(data, curVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class encapsulating the logic for migrating data from a given version up to
|
||||
* the current version listed in `package.json`.
|
||||
*
|
||||
* Note that, for any version converter, the corresponding `applyMigration`
|
||||
* function would only need to be changed once when the first migration for a
|
||||
* given version is introduced. Similarly, a version file (within the `versions`
|
||||
* folder) would only need to be created for a version once with the appropriate
|
||||
* array nomenclature.
|
||||
*/
|
||||
abstract class VersionConverter {
|
||||
/**
|
||||
* Iterates through an array of designated migration functions that are each
|
||||
* called one by one to transform the data.
|
||||
* @param data The data to be operated on
|
||||
* @param migrationArr An array of functions that will transform the incoming data
|
||||
*/
|
||||
callMigrators(data: any, migrationArr: readonly any[]) {
|
||||
for (const migrate of migrationArr) {
|
||||
migrate(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies any version-agnostic data sanitation as defined within the function
|
||||
* body.
|
||||
* @param data The data to be operated on
|
||||
*/
|
||||
applyStaticPreprocessors(_data: any): void {}
|
||||
|
||||
/**
|
||||
* Uses the current version the incoming data to determine the starting point
|
||||
* of the migration which will cascade up to the latest version, calling the
|
||||
* necessary migration functions in the process.
|
||||
* @param data The data to be operated on
|
||||
* @param curVersion [0] Current major version
|
||||
* [1] Current minor version
|
||||
* [2] Current patch version
|
||||
*/
|
||||
abstract applyMigration(data: any, curVersion: number[]): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating {@linkcode SessionSaveData} from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SessionVersionConverter extends VersionConverter {
|
||||
override applyStaticPreprocessors(data: SessionSaveData): void {
|
||||
// Always sanitize money as a safeguard
|
||||
data.money = Math.floor(data.money);
|
||||
}
|
||||
|
||||
override applyMigration(data: SessionSaveData, curVersion: number[]): void {
|
||||
const [curMajor, curMinor, curPatch] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 5) {
|
||||
console.log("Applying v1.0.4 session data migration!");
|
||||
this.callMigrators(data, v1_0_4.sessionMigrators);
|
||||
}
|
||||
}
|
||||
if (curMinor <= 1) {
|
||||
console.log("Applying v1.1.0 session data migration!");
|
||||
this.callMigrators(data, v1_1_0.sessionMigrators);
|
||||
}
|
||||
if (curMinor < 7) {
|
||||
console.log("Applying v1.7.0 session data migration!");
|
||||
this.callMigrators(data, v1_7_0.sessionMigrators);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Session data successfully migrated to v${version}!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating {@linkcode SystemSaveData} from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SystemVersionConverter extends VersionConverter {
|
||||
override applyMigration(data: SystemSaveData, curVersion: number[]): void {
|
||||
const [curMajor, curMinor, curPatch] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 4) {
|
||||
console.log("Applying v1.0.4 system data migraton!");
|
||||
this.callMigrators(data, v1_0_4.systemMigrators);
|
||||
}
|
||||
}
|
||||
if (curMinor <= 1) {
|
||||
console.log("Applying v1.1.0 system data migraton!");
|
||||
this.callMigrators(data, v1_1_0.systemMigrators);
|
||||
}
|
||||
if (curMinor < 7) {
|
||||
console.log("Applying v1.7.0 system data migration!");
|
||||
this.callMigrators(data, v1_7_0.systemMigrators);
|
||||
}
|
||||
if (curMinor === 8) {
|
||||
if (curPatch <= 2) {
|
||||
console.log("Applying v1.8.3 system data migration!");
|
||||
this.callMigrators(data, v1_8_3.systemMigrators);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`System data successfully migrated to v${version}!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class encapsulating the logic for migrating settings data from
|
||||
* a given version up to the current version listed in `package.json`.
|
||||
* @extends VersionConverter
|
||||
*/
|
||||
class SettingsVersionConverter extends VersionConverter {
|
||||
override applyMigration(data: Object, curVersion: number[]): void {
|
||||
const [curMajor, curMinor, curPatch] = curVersion;
|
||||
|
||||
if (curMajor === 1) {
|
||||
if (curMinor === 0) {
|
||||
if (curPatch <= 4) {
|
||||
console.log("Applying v1.0.4 settings data migraton!");
|
||||
this.callMigrators(data, v1_0_4.settingsMigrators);
|
||||
}
|
||||
}
|
||||
if (curMinor <= 1) {
|
||||
console.log("Applying v1.1.0 settings data migraton!");
|
||||
this.callMigrators(data, v1_1_0.settingsMigrators);
|
||||
}
|
||||
if (curMinor < 7) {
|
||||
console.log("Applying v1.7.0 settings data migration!");
|
||||
this.callMigrators(data, v1_7_0.settingsMigrators);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Settings data successfully migrated to v${version}!`);
|
||||
if (isCurrentVersionHigher) {
|
||||
applyMigrators(settingsMigrators, data, prevVersion);
|
||||
console.log(`Settings successfully migrated to v${LATEST_VERSION}!`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,18 @@ import { AbilityAttr, defaultStarterSpecies, DexAttr } from "#app/system/game-da
|
|||
import { allSpecies } from "#app/data/pokemon-species";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator";
|
||||
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
|
||||
|
||||
export const systemMigrators = [
|
||||
/**
|
||||
* Migrate ability starter data if empty for caught species.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function migrateAbilityData(data: SystemSaveData) {
|
||||
/**
|
||||
* Migrate ability starter data if empty for caught species.
|
||||
* @param data - {@linkcode SystemSaveData}
|
||||
*/
|
||||
const migrateAbilityData: SystemSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
if (data.starterData && data.dexData) {
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
if (data.dexData[sd]?.caughtAttr && data.starterData[sd] && !data.starterData[sd].abilityAttr) {
|
||||
data.starterData[sd].abilityAttr = 1;
|
||||
|
@ -20,12 +23,15 @@ export const systemMigrators = [
|
|||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate legendary Pokémon statistics if they are missing.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function fixLegendaryStats(data: SystemSaveData) {
|
||||
/**
|
||||
* Populate legendary Pokémon statistics if they are missing.
|
||||
* @param data - {@linkcode SystemSaveData}
|
||||
*/
|
||||
const fixLegendaryStats: SystemSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
if (
|
||||
data.gameStats &&
|
||||
data.gameStats.legendaryPokemonCaught !== undefined &&
|
||||
|
@ -34,7 +40,6 @@ export const systemMigrators = [
|
|||
data.gameStats.subLegendaryPokemonSeen = 0;
|
||||
data.gameStats.subLegendaryPokemonCaught = 0;
|
||||
data.gameStats.subLegendaryPokemonHatched = 0;
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
allSpecies
|
||||
.filter(s => s.subLegendary)
|
||||
.forEach(s => {
|
||||
|
@ -66,12 +71,15 @@ export const systemMigrators = [
|
|||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlock all starters' first ability and female gender option.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function fixStarterData(data: SystemSaveData) {
|
||||
/**
|
||||
* Unlock all starters' first ability and female gender option.
|
||||
* @param data - {@linkcode SystemSaveData}
|
||||
*/
|
||||
const fixStarterData: SystemSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
if (!isNullOrUndefined(data.starterData)) {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
if (data.starterData[starterId]?.abilityAttr) {
|
||||
|
@ -83,17 +91,22 @@ export const systemMigrators = [
|
|||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const systemMigrators: Readonly<SystemSaveMigrator[]> = [
|
||||
migrateAbilityData,
|
||||
fixLegendaryStats,
|
||||
fixStarterData,
|
||||
] as const;
|
||||
|
||||
export const settingsMigrators = [
|
||||
/**
|
||||
* Migrate from "REROLL_TARGET" property to {@linkcode
|
||||
* SettingKeys.Shop_Cursor_Target}.
|
||||
* @param data the `settings` object
|
||||
*/
|
||||
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO: fix the type to not be object...
|
||||
function fixRerollTarget(data: Object) {
|
||||
/**
|
||||
* Migrate from `REROLL_TARGET` property to {@linkcode SettingKeys.Shop_Cursor_Target}
|
||||
* @param data - The `settings` object
|
||||
*/
|
||||
const fixRerollTarget: SettingsSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings
|
||||
migrate: (data: Object): void => {
|
||||
if (data.hasOwnProperty("REROLL_TARGET") && !data.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) {
|
||||
data[SettingKeys.Shop_Cursor_Target] = data["REROLL_TARGET"];
|
||||
// biome-ignore lint/performance/noDelete: intentional
|
||||
|
@ -101,16 +114,20 @@ export const settingsMigrators = [
|
|||
localStorage.setItem("settings", JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const sessionMigrators = [
|
||||
/**
|
||||
* Converts old lapsing modifiers (battle items, lures, and Dire Hit) and
|
||||
* other miscellaneous modifiers (vitamins, White Herb) to any new class
|
||||
* names and/or change in reload arguments.
|
||||
* @param data {@linkcode SessionSaveData}
|
||||
*/
|
||||
function migrateModifiers(data: SessionSaveData) {
|
||||
export const settingsMigrators: Readonly<SettingsSaveMigrator[]> = [fixRerollTarget] as const;
|
||||
|
||||
/**
|
||||
* Converts old lapsing modifiers (battle items, lures, and Dire Hit) and
|
||||
* other miscellaneous modifiers (vitamins, White Herb) to any new class
|
||||
* names and/or change in reload arguments.
|
||||
* @param data - {@linkcode SessionSaveData}
|
||||
*/
|
||||
const migrateModifiers: SessionSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: necessary?
|
||||
migrate: (data: SessionSaveData): void => {
|
||||
for (const m of data.modifiers) {
|
||||
if (m.className === "PokemonBaseStatModifier") {
|
||||
m.className = "BaseStatModifier";
|
||||
|
@ -163,12 +180,11 @@ export const sessionMigrators = [
|
|||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Converts old Pokemon natureOverride and mysteryEncounterData
|
||||
* to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead.
|
||||
* @param data {@linkcode SessionSaveData}
|
||||
*/
|
||||
function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) {
|
||||
};
|
||||
|
||||
const migrateCustomPokemonData: SessionSaveMigrator = {
|
||||
version: "1.0.4",
|
||||
migrate: (data: SessionSaveData): void => {
|
||||
// Fix Pokemon nature overrides and custom data migration
|
||||
for (const pokemon of data.party) {
|
||||
if (pokemon["mysteryEncounterPokemonData"]) {
|
||||
|
@ -186,4 +202,6 @@ export const sessionMigrators = [
|
|||
}
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const sessionMigrators: Readonly<SessionSaveMigrator[]> = [migrateModifiers, migrateCustomPokemonData] as const;
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
export const systemMigrators = [] as const;
|
||||
|
||||
export const settingsMigrators = [] as const;
|
||||
|
||||
export const sessionMigrators = [] as const;
|
|
@ -1,15 +1,18 @@
|
|||
import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator";
|
||||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { DexAttr, type SessionSaveData, type SystemSaveData } from "#app/system/game-data";
|
||||
import * as Utils from "#app/utils";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
|
||||
export const systemMigrators = [
|
||||
/**
|
||||
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
|
||||
* unlock the default form.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function migrateUnselectableForms(data: SystemSaveData) {
|
||||
/**
|
||||
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
|
||||
* unlock the default form.
|
||||
* @param data - {@linkcode SystemSaveData}
|
||||
*/
|
||||
const migrateUnselectableForms: SystemSaveMigrator = {
|
||||
version: "1.7.0",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
if (data.starterData && data.dexData) {
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
const caughtAttr = data.dexData[sd]?.caughtAttr;
|
||||
|
@ -30,12 +33,13 @@ export const systemMigrators = [
|
|||
});
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const settingsMigrators = [] as const;
|
||||
export const systemMigrators: Readonly<SystemSaveMigrator[]> = [migrateUnselectableForms] as const;
|
||||
|
||||
export const sessionMigrators = [
|
||||
function migrateTera(data: SessionSaveData) {
|
||||
const migrateTera: SessionSaveMigrator = {
|
||||
version: "1.7.0",
|
||||
migrate: (data: SessionSaveData): void => {
|
||||
for (let i = 0; i < data.modifiers.length; ) {
|
||||
if (data.modifiers[i].className === "TerastallizeModifier") {
|
||||
data.party.forEach(p => {
|
||||
|
@ -63,15 +67,17 @@ export const sessionMigrators = [
|
|||
}
|
||||
|
||||
data.party.forEach(p => {
|
||||
if (Utils.isNullOrUndefined(p.teraType)) {
|
||||
if (isNullOrUndefined(p.teraType)) {
|
||||
p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1;
|
||||
}
|
||||
});
|
||||
|
||||
data.enemyParty.forEach(p => {
|
||||
if (Utils.isNullOrUndefined(p.teraType)) {
|
||||
if (isNullOrUndefined(p.teraType)) {
|
||||
p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1;
|
||||
}
|
||||
});
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const sessionMigrators: Readonly<SessionSaveMigrator[]> = [migrateTera] as const;
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { DexAttr, type SystemSaveData } from "#app/system/game-data";
|
||||
import { Species } from "#enums/species";
|
||||
|
||||
export const systemMigrators = [
|
||||
/**
|
||||
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
|
||||
* unlock the default form.
|
||||
* @param data {@linkcode SystemSaveData}
|
||||
*/
|
||||
function migratePichuForms(data: SystemSaveData) {
|
||||
/**
|
||||
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
|
||||
* unlock the default form.
|
||||
* @param data - {@linkcode SystemSaveData}
|
||||
*/
|
||||
const migratePichuForms: SystemSaveMigrator = {
|
||||
version: "1.8.3",
|
||||
migrate: (data: SystemSaveData): void => {
|
||||
if (data.starterData && data.dexData) {
|
||||
// This is Pichu's Pokédex number
|
||||
const sd = 172;
|
||||
|
@ -23,8 +25,6 @@ export const systemMigrators = [
|
|||
}
|
||||
}
|
||||
},
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const settingsMigrators = [] as const;
|
||||
|
||||
export const sessionMigrators = [] as const;
|
||||
export const systemMigrators: Readonly<SystemSaveMigrator[]> = [migratePichuForms] as const;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Immunity", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove poison when gained", async () => {
|
||||
game.override.ability(Abilities.IMMUNITY)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.POISON);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.POISON);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Insomnia", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove sleep when gained", async () => {
|
||||
game.override.ability(Abilities.INSOMNIA)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.SLEEP);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Limber", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove paralysis when gained", async () => {
|
||||
game.override.ability(Abilities.LIMBER)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.PARALYSIS);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Magma Armor", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove freeze when gained", async () => {
|
||||
game.override.ability(Abilities.MAGMA_ARMOR)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.FREEZE);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.FREEZE);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,69 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("Abilities - Oblivious", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove taunt when gained", async () => {
|
||||
game.override.ability(Abilities.OBLIVIOUS)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.addTag(BattlerTagType.TAUNT);
|
||||
expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeTruthy();
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should remove infatuation when gained", async () => {
|
||||
game.override.ability(Abilities.OBLIVIOUS)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
vi.spyOn(enemy!, "isOppositeGender").mockReturnValue(true);
|
||||
enemy?.addTag(BattlerTagType.INFATUATED, 5, Moves.JUDGMENT, game.scene.getPlayerPokemon()?.id); // sourceID needs to be defined
|
||||
expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeTruthy();
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeFalsy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Own Tempo", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove confusion when gained", async () => {
|
||||
game.override.ability(Abilities.OWN_TEMPO)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.addTag(BattlerTagType.CONFUSED);
|
||||
expect(enemy?.getTag(BattlerTagType.CONFUSED)).toBeTruthy();
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.getTag(BattlerTagType.CONFUSED)).toBeFalsy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Thermal Exchange", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove burn when gained", async () => {
|
||||
game.override.ability(Abilities.THERMAL_EXCHANGE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.BURN);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.BURN);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Vital Spirit", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove sleep when gained", async () => {
|
||||
game.override.ability(Abilities.INSOMNIA)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.SLEEP);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Water Bubble", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove burn when gained", async () => {
|
||||
game.override.ability(Abilities.THERMAL_EXCHANGE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.BURN);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.BURN);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Water Veil", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should remove burn when gained", async () => {
|
||||
game.override.ability(Abilities.THERMAL_EXCHANGE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.moveset(Moves.SKILL_SWAP)
|
||||
.enemyMoveset(Moves.SPLASH),
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon();
|
||||
enemy?.trySetStatus(StatusEffect.BURN);
|
||||
expect(enemy?.status?.effect).toBe(StatusEffect.BURN);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy?.status).toBeNull();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue