[BUG] fixes #5472 - transform on reload (#5508)

* [BUG] fixes #5472 - transform on reload

* Fix the bug where transformed pokemon failed to load sprite on reload if it was not the base form

* Now properly loads the transformed sprite assets during summon phase
This commit is contained in:
José Serrado Marques 2025-03-23 19:07:14 +00:00 committed by GitHub
parent 4b8f1df8cd
commit 1e876ec595
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 160 additions and 4 deletions

View File

@ -5575,7 +5575,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
this.resetBattleSummonData(); this.resetBattleSummonData();
if (this.summonDataPrimer) { if (this.summonDataPrimer) {
for (const k of Object.keys(this.summonData)) { for (const k of Object.keys(this.summonDataPrimer)) {
if (this.summonDataPrimer[k]) { if (this.summonDataPrimer[k]) {
this.summonData[k] = this.summonDataPrimer[k]; this.summonData[k] = this.summonDataPrimer[k];
} }

View File

@ -195,6 +195,10 @@ export class SummonPhase extends PartyMemberPokemonPhase {
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
pokemon.getSprite().clearTint(); pokemon.getSprite().clearTint();
pokemon.resetSummonData(); pokemon.resetSummonData();
// necessary to stay transformed during wild waves
if (pokemon.summonData?.speciesForm) {
pokemon.loadAssets(false);
}
globalScene.time.delayedCall(1000, () => this.end()); globalScene.time.delayedCall(1000, () => this.end());
}, },
}); });

View File

@ -3,7 +3,7 @@ import { globalScene } from "#app/global-scene";
import type { Gender } from "../data/gender"; import type { Gender } from "../data/gender";
import type { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
import type { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { getPokemonSpecies } from "../data/pokemon-species"; import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species";
import { Status } from "../data/status-effect"; import { Status } from "../data/status-effect";
import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon"; import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon";
import { TrainerSlot } from "../data/trainer-config"; import { TrainerSlot } from "../data/trainer-config";
@ -14,6 +14,7 @@ import { Moves } from "#enums/moves";
import type { Species } from "#enums/species"; import type { Species } from "#enums/species";
import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import type { PokemonType } from "#enums/pokemon-type"; import type { PokemonType } from "#enums/pokemon-type";
import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms";
export default class PokemonData { export default class PokemonData {
public id: number; public id: number;
@ -63,6 +64,7 @@ export default class PokemonData {
public bossSegments?: number; public bossSegments?: number;
public summonData: PokemonSummonData; public summonData: PokemonSummonData;
public summonDataSpeciesFormIndex: number;
/** Data that can customize a Pokemon in non-standard ways from its Species */ /** Data that can customize a Pokemon in non-standard ways from its Species */
public customPokemonData: CustomPokemonData; public customPokemonData: CustomPokemonData;
@ -145,8 +147,9 @@ export default class PokemonData {
this.moveset = sourcePokemon.moveset; this.moveset = sourcePokemon.moveset;
if (!forHistory) { if (!forHistory) {
this.status = sourcePokemon.status; this.status = sourcePokemon.status;
if (this.player) { if (this.player && sourcePokemon.summonData) {
this.summonData = sourcePokemon.summonData; this.summonData = sourcePokemon.summonData;
this.summonDataSpeciesFormIndex = this.getSummonDataSpeciesFormIndex();
} }
} }
} else { } else {
@ -170,6 +173,8 @@ export default class PokemonData {
this.summonData.ability = source.summonData.ability; this.summonData.ability = source.summonData.ability;
this.summonData.moveset = source.summonData.moveset?.map(m => PokemonMove.loadMove(m)); this.summonData.moveset = source.summonData.moveset?.map(m => PokemonMove.loadMove(m));
this.summonData.types = source.summonData.types; this.summonData.types = source.summonData.types;
this.summonData.speciesForm = source.summonData.speciesForm;
this.summonDataSpeciesFormIndex = source.summonDataSpeciesFormIndex;
if (source.summonData.tags) { if (source.summonData.tags) {
this.summonData.tags = source.summonData.tags?.map(t => loadBattlerTag(t)); this.summonData.tags = source.summonData.tags?.map(t => loadBattlerTag(t));
@ -213,8 +218,28 @@ export default class PokemonData {
this, this,
); );
if (this.summonData) { if (this.summonData) {
// when loading from saved session, recover summonData.speciesFrom and form index species object
// used to stay transformed on reload session
if (this.summonData.speciesForm) {
this.summonData.speciesForm = getPokemonSpeciesForm(
this.summonData.speciesForm.speciesId,
this.summonDataSpeciesFormIndex,
);
}
ret.primeSummonData(this.summonData); ret.primeSummonData(this.summonData);
} }
return ret; return ret;
} }
/**
* Method to save summon data species form index
* Necessary in case the pokemon is transformed
* to reload the correct form
*/
getSummonDataSpeciesFormIndex(): number {
if (this.summonData.speciesForm) {
return this.summonData.speciesForm.formIndex;
}
return 0;
}
} }

View File

@ -127,4 +127,63 @@ describe("Abilities - Imposter", () => {
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
}); });
it("should persist transformed attributes across reloads", async () => {
game.override.moveset([Moves.ABSORB]);
await game.classicMode.startBattle([Species.DITTO]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SPLASH);
await game.doKillOpponents();
await game.toNextWave();
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
const playerReloaded = game.scene.getPlayerPokemon()!;
const playerMoveset = player.getMoveset();
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
expect(playerReloaded.getAbility()).toBe(enemy.getAbility());
expect(playerReloaded.getGender()).toBe(enemy.getGender());
expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP));
for (const s of EFFECTIVE_STATS) {
expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false));
}
expect(playerMoveset.length).toEqual(1);
expect(playerMoveset[0]?.moveId).toEqual(Moves.SPLASH);
});
it("should stay transformed with the correct form after reload", async () => {
game.override.moveset([Moves.ABSORB]);
game.override.enemySpecies(Species.UNOWN);
await game.classicMode.startBattle([Species.DITTO]);
const enemy = game.scene.getEnemyPokemon()!;
// change form
enemy.species.forms[5];
enemy.species.formIndex = 5;
game.move.select(Moves.SPLASH);
await game.doKillOpponents();
await game.toNextWave();
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
const playerReloaded = game.scene.getPlayerPokemon()!;
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex);
});
}); });

View File

@ -6,6 +6,7 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { BattlerIndex } from "#app/battle";
// TODO: Add more tests once Transform is fully implemented // TODO: Add more tests once Transform is fully implemented
describe("Moves - Transform", () => { describe("Moves - Transform", () => {
@ -58,7 +59,7 @@ describe("Moves - Transform", () => {
} }
const playerMoveset = player.getMoveset(); const playerMoveset = player.getMoveset();
const enemyMoveset = player.getMoveset(); const enemyMoveset = enemy.getMoveset();
expect(playerMoveset.length).toBe(enemyMoveset.length); expect(playerMoveset.length).toBe(enemyMoveset.length);
for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) {
@ -127,4 +128,71 @@ describe("Moves - Transform", () => {
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
}); });
it("should persist transformed attributes across reloads", async () => {
game.override.enemyMoveset([]).moveset([]);
await game.classicMode.startBattle([Species.DITTO]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
game.move.changeMoveset(player, Moves.TRANSFORM);
game.move.changeMoveset(enemy, Moves.MEMENTO);
game.move.select(Moves.TRANSFORM);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
const playerReloaded = game.scene.getPlayerPokemon()!;
const playerMoveset = player.getMoveset();
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
expect(playerReloaded.getAbility()).toBe(enemy.getAbility());
expect(playerReloaded.getGender()).toBe(enemy.getGender());
expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP));
for (const s of EFFECTIVE_STATS) {
expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false));
}
expect(playerMoveset.length).toEqual(1);
expect(playerMoveset[0]?.moveId).toEqual(Moves.MEMENTO);
});
it("should stay transformed with the correct form after reload", async () => {
game.override.enemyMoveset([]).moveset([]);
game.override.enemySpecies(Species.DARMANITAN);
await game.classicMode.startBattle([Species.DITTO]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
// change form
enemy.species.forms[1];
enemy.species.formIndex = 1;
game.move.changeMoveset(player, Moves.TRANSFORM);
game.move.changeMoveset(enemy, Moves.MEMENTO);
game.move.select(Moves.TRANSFORM);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave();
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession();
const playerReloaded = game.scene.getPlayerPokemon()!;
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex);
});
}); });