Re-added BattleSummonData as TempSummonData

This commit is contained in:
Bertie690 2025-05-01 10:36:28 -04:00
parent b6b8aa0e39
commit 448109d10f
12 changed files with 122 additions and 111 deletions

View File

@ -8010,7 +8010,7 @@ export class MoveCondition {
export class FirstMoveCondition extends MoveCondition {
constructor() {
super((user, _target, _move) => user.summonData.waveTurnCount === 1);
super((user, _target, _move) => user.tempSummonData.waveTurnCount === 1);
}
getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number {

View File

@ -3,7 +3,7 @@ import type Pokemon from "../field/pokemon";
import { StatusEffect } from "#enums/status-effect";
import { allMoves } from "./moves/move";
import { MoveCategory } from "#enums/MoveCategory";
import type { Constructor, nil } from "#app/utils/common";
import { isNullOrUndefined, type Constructor, type nil } from "#app/utils/common";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
@ -369,12 +369,10 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrigger {
public movePredicate: (m: Moves) => boolean;
public used: boolean;
constructor(move: Moves | ((m: Moves) => boolean), used = true) {
constructor(move: Moves | ((m: Moves) => boolean)) {
super();
this.movePredicate = typeof move === "function" ? move : (m: Moves) => m === move;
this.used = used;
}
}
@ -383,7 +381,7 @@ export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigge
canChange(pokemon: Pokemon): boolean {
const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
return !!command?.move && this.movePredicate(command.move.move) === this.used;
return !isNullOrUndefined(command?.move) && this.movePredicate(command.move.move);
}
}
@ -391,9 +389,7 @@ export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigg
description = i18next.t("pokemonEvolutions:Forms.postMove");
canChange(pokemon: Pokemon): boolean {
return (
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
);
return this.movePredicate(pokemon.getLastXMoves()[0].move);
}
}

View File

@ -368,6 +368,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public battleData: PokemonBattleData = new PokemonBattleData();
/** Data that resets on switch or battle end (stat stages, battler tags, etc.) */
public summonData: PokemonSummonData = new PokemonSummonData();
/** Similar to {@linkcode PokemonSummonData}, but is reset on reload (not saved to file). */
public tempSummonData: PokemonTempSummonData = new PokemonTempSummonData();
/** Wave data correponding to moves/ability information revealed */
public waveData: PokemonWaveData = new PokemonWaveData();
/** Per-turn data like hit count & flinch tracking */
@ -1427,7 +1429,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
getStat(stat: PermanentStat, bypassSummonData = true): number {
if (
!bypassSummonData &&
this.summonData &&
this.summonData.stats[stat] !== 0
) {
return this.summonData.stats[stat];
@ -2156,10 +2157,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
// the type added to Pokemon from moves like Forest's Curse or Trick Or Treat
// check type added to Pokemon from moves like Forest's Curse or Trick Or Treat
if (
!ignoreOverride &&
this.summonData &&
this.summonData.addedType &&
!types.includes(this.summonData.addedType)
) {
@ -4967,9 +4967,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
transferTagsBySourceId(sourceId: number, newSourceId: number): void {
this.summonData.tags
.filter(t => t.sourceId === sourceId)
.forEach(t => (t.sourceId = newSourceId));
this.summonData.tags.forEach(t => {
if (t.sourceId === sourceId) {
t.sourceId = newSourceId;
}
})
}
/**
@ -5646,7 +5648,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
/**
* Reset this Pokemon's {@linkcode PokemonSummonData | SummonData} and {@linkcode PokemonTempSummonData | TempSummonData}
* in preparation for switching pokemon, as well as removing any relevant on-switch tags.
*/
resetSummonData(): void {
const illusion: IllusionData | null = this.summonData.illusion;
if (this.summonData.speciesForm) {
@ -5654,8 +5659,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.updateFusionPalette();
}
this.summonData = new PokemonSummonData();
this.tempSummonData = new PokemonTempSummonData();
this.setSwitchOutStatus(false);
// TODO: Why do we remove leech seed on resetting summon data? That should be a BattlerTag thing...
// TODO: Why do we tick down leech seed on switch
if (this.getTag(BattlerTagType.SEEDED)) {
this.lapseTag(BattlerTagType.SEEDED);
}
@ -7751,7 +7758,8 @@ export class PokemonSummonData {
public tags: BattlerTag[] = [];
public abilitySuppressed = false;
// Overrides for transform
// Overrides for transform.
// TODO: Move these into a separate class & add rage fist hit count
public speciesForm: PokemonSpeciesForm | null = null;
public fusionSpeciesForm: PokemonSpeciesForm | null = null;
public ability: Abilities | undefined;
@ -7772,16 +7780,6 @@ export class PokemonSummonData {
/** Array containing all berries eaten in the last turn; used by {@linkcode Abilities.CUD_CHEW} */
public berriesEatenLast: BerryType[] = [];
/**
* The number of turns this pokemon has spent in the active position since entering the battle.
* Currently exclusively used for positioning the battle cursor.
*/
public turnCount = 1;
/**
* The number of turns this pokemon has spent in the active position since starting a new wave.
* Used for most "first turn only" conditions ({@linkcode Moves.FAKE_OUT | Fake Out}, {@linkcode Moves.FIRST_IMPRESSION | First Impression}, etc.)
*/
public waveTurnCount = 1;
/**
* An array of all moves this pokemon has used since entering the battle.
* Used for most moves and abilities that check prior move usage or copy already-used moves.
@ -7796,6 +7794,26 @@ export class PokemonSummonData {
}
}
// TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added
export class PokemonTempSummonData {
/**
* The number of turns this pokemon has spent without switching out.
* Only currently used for positioning the battle cursor.
*/
turnCount: number = 1;
/**
* The number of turns this pokemon has spent in the active position since the start of the wave
* without switching out.
* Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file.
* Used to evaluate "first turn only" conditions such as
* {@linkcode Moves.FAKE_OUT | Fake Out} and {@linkcode Moves.FIRST_IMPRESSION | First Impression}).
*/
waveTurnCount = 1;
}
/**
* Persistent data for a {@linkcode Pokemon}.
* Resets at the start of a new battle (but not on switch).
@ -7819,7 +7837,7 @@ export class PokemonBattleData {
/**
* Temporary data for a {@linkcode Pokemon}.
* Resets on new wave/battle start.
* Resets on new wave/battle start (but not on switch).
*/
export class PokemonWaveData {
/** Whether the pokemon has endured due to a {@linkcode BattlerTagType.ENDURE_TOKEN} */
@ -7866,7 +7884,7 @@ export class PokemonTurnData {
public extraTurns = 0;
/**
* All berries eaten by this pokemon in this turn.
* Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode Abilities.CUD_CHEW) on turn end.
* Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode Abilities.CUD_CHEW} on turn end.
* @see {@linkcode PokemonSummonData.berriesEatenLast}
*/
public berriesEaten: BerryType[] = []

View File

@ -60,7 +60,7 @@ export class BattleEndPhase extends BattlePhase {
for (const pokemon of globalScene.getField()) {
if (pokemon) {
pokemon.summonData.waveTurnCount = 1;
pokemon.tempSummonData.waveTurnCount = 1;
}
}

View File

@ -6,8 +6,7 @@ type PokemonFunc = (pokemon: Pokemon) => void;
export abstract class FieldPhase extends BattlePhase {
executeForAll(func: PokemonFunc): void {
const field = globalScene.getField(true).filter(p => p.summonData);
for (const pokemon of field) {
for (const pokemon of globalScene.getField(true)) {
func(pokemon);
}
}

View File

@ -229,8 +229,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
// Lapse any residual flinches/endures but ignore all other turn-end battle tags
const includedLapseTags = [BattlerTagType.FLINCHED, BattlerTagType.ENDURING];
const field = globalScene.getField(true).filter(p => p.summonData);
field.forEach(pokemon => {
globalScene.getField(true).forEach(pokemon => {
const tags = pokemon.summonData.tags;
tags
.filter(

View File

@ -33,10 +33,10 @@ export class SwitchSummonPhase extends SummonPhase {
* @param fieldIndex - Position on the battle field
* @param slotIndex - The index of pokemon (in party of 6) to switch into
* @param doReturn - Whether to render "comeback" dialogue
* @param player - (Optional) `true` if the switch is from the player
* @param player - Whether the switch came from the player or enemy; default `true`
*/
constructor(switchType: SwitchType, fieldIndex: number, slotIndex: number, doReturn: boolean, player?: boolean) {
super(fieldIndex, player !== undefined ? player : true);
constructor(switchType: SwitchType, fieldIndex: number, slotIndex: number, doReturn: boolean, player = true) {
super(fieldIndex, player);
this.switchType = switchType;
this.slotIndex = slotIndex;
@ -121,14 +121,23 @@ export class SwitchSummonPhase extends SummonPhase {
switchAndSummon() {
const party = this.player ? this.getParty() : globalScene.getEnemyParty();
const switchedInPokemon = party[this.slotIndex];
const switchedInPokemon: Pokemon | undefined = party[this.slotIndex];
this.lastPokemon = this.getPokemon();
applyPreSummonAbAttrs(PreSummonAbAttr, switchedInPokemon);
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
if (this.switchType === SwitchType.BATON_PASS && switchedInPokemon) {
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach(enemyPokemon =>
if (!switchedInPokemon) {
this.end();
return;
}
if (this.switchType === SwitchType.BATON_PASS) {
// If switching via baton pass, update opposing tags coming from the prior pokemon
(this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) =>
enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id),
);
// If the recipient pokemon lacks a baton, give our baton to it during the swap
if (
!globalScene.findModifier(
m =>
@ -141,14 +150,8 @@ export class SwitchSummonPhase extends SummonPhase {
m instanceof SwitchEffectTransferModifier &&
(m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id,
) as SwitchEffectTransferModifier;
if (
batonPassModifier &&
!globalScene.findModifier(
m =>
m instanceof SwitchEffectTransferModifier &&
(m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id,
)
) {
if (batonPassModifier) {
globalScene.tryTransferHeldItemModifier(
batonPassModifier,
switchedInPokemon,
@ -161,49 +164,48 @@ export class SwitchSummonPhase extends SummonPhase {
}
}
}
if (switchedInPokemon) {
party[this.slotIndex] = this.lastPokemon;
party[this.fieldIndex] = switchedInPokemon;
const showTextAndSummon = () => {
globalScene.ui.showText(
this.player
? i18next.t("battle:playerGo", {
pokemonName: getPokemonNameWithAffix(switchedInPokemon),
})
: i18next.t("battle:trainerGo", {
trainerName: globalScene.currentBattle.trainer?.getName(
!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER,
),
pokemonName: this.getPokemon().getNameToRender(),
}),
);
/**
* If this switch is passing a Substitute, make the switched Pokemon match the returned Pokemon's state as it left.
* Otherwise, clear any persisting tags on the returned Pokemon.
*/
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
const substitute = this.lastPokemon.getTag(SubstituteTag);
if (substitute) {
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
switchedInPokemon.setAlpha(0.5);
}
} else {
switchedInPokemon.resetSummonData();
party[this.slotIndex] = this.lastPokemon;
party[this.fieldIndex] = switchedInPokemon;
const showTextAndSummon = () => {
globalScene.ui.showText(
this.player
? i18next.t("battle:playerGo", {
pokemonName: getPokemonNameWithAffix(switchedInPokemon),
})
: i18next.t("battle:trainerGo", {
trainerName: globalScene.currentBattle.trainer?.getName(
!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER,
),
pokemonName: this.getPokemon().getNameToRender(),
}),
);
/**
* If this switch is passing a Substitute, make the switched Pokemon matches the returned Pokemon's state as it left.
* Otherwise, clear any persisting tags on the returned Pokemon.
*/
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
const substitute = this.lastPokemon.getTag(SubstituteTag);
if (substitute) {
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
switchedInPokemon.setAlpha(0.5);
}
this.summon();
};
if (this.player) {
showTextAndSummon();
} else {
globalScene.time.delayedCall(1500, () => {
this.hideEnemyTrainer();
globalScene.pbTrayEnemy.hide();
showTextAndSummon();
});
switchedInPokemon.resetSummonData();
}
this.summon();
};
if (this.player) {
showTextAndSummon();
} else {
this.end();
globalScene.time.delayedCall(1500, () => {
this.hideEnemyTrainer();
globalScene.pbTrayEnemy.hide();
showTextAndSummon();
});
}
}
@ -221,15 +223,15 @@ export class SwitchSummonPhase extends SummonPhase {
const lastPokemonHasForceSwitchAbAttr =
this.lastPokemon.hasAbilityWithAttr(PostDamageForceSwitchAbAttr) && !this.lastPokemon.isFainted();
// Compensate for turn spent summoning
// Or compensate for force switch move if switched out pokemon is not fainted
// Compensate for turn spent summoning/forced switch if switched out pokemon is not fainted.
// Needed as we increment turn counters in `TurnEndPhase`.
if (
currentCommand === Command.POKEMON ||
lastPokemonIsForceSwitchedAndNotFainted ||
lastPokemonHasForceSwitchAbAttr
) {
pokemon.summonData.turnCount--;
pokemon.summonData.waveTurnCount--;
pokemon.tempSummonData.turnCount--;
pokemon.tempSummonData.waveTurnCount--;
}
if (this.switchType === SwitchType.BATON_PASS && pokemon) {
@ -247,7 +249,7 @@ export class SwitchSummonPhase extends SummonPhase {
pokemon.turnData.switchedInThisTurn = true;
}
this.lastPokemon?.resetSummonData();
this.lastPokemon.resetSummonData();
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
// Reverts to weather-based forms when weather suppressors (Cloud Nine/Air Lock) are switched out

View File

@ -56,8 +56,8 @@ export class TurnEndPhase extends FieldPhase {
globalScene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon);
globalScene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
pokemon.summonData.turnCount++;
pokemon.summonData.waveTurnCount++;
pokemon.tempSummonData.turnCount++;
pokemon.tempSummonData.waveTurnCount++;
};
this.executeForAll(handlePokemon);

View File

@ -72,19 +72,16 @@ export class TurnStartPhase extends FieldPhase {
// This occurs before the main loop because of battles with more than two Pokemon
const battlerBypassSpeed = {};
globalScene
.getField(true)
.filter(p => p.summonData)
.map(p => {
const bypassSpeed = new BooleanHolder(false);
const canCheckHeldItems = new BooleanHolder(true);
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed);
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
}
battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed;
});
globalScene.getField(true).map(p => {
const bypassSpeed = new BooleanHolder(false);
const canCheckHeldItems = new BooleanHolder(true);
applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed);
applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems);
if (canCheckHeldItems.value) {
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
}
battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed;
});
// The function begins sorting orderedTargets based on command priority, move priority, and possible speed bypasses.
// Non-FIGHT commands (SWITCH, BALL, RUN) have a higher command priority and will always occur before any FIGHT commands.

View File

@ -794,7 +794,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO);
nameTextWidth = nameSizeTest.displayWidth;
const gender = pokemon.summonData?.illusion?.gender ?? pokemon.gender;
const gender = pokemon.summonData.illusion?.gender ?? pokemon.gender;
while (
nameTextWidth >
(this.player || !this.boss ? 60 : 98) -

View File

@ -127,7 +127,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
messageHandler.commandWindow.setVisible(false);
messageHandler.movesWindowContainer.setVisible(true);
const pokemon = (globalScene.getCurrentPhase() as CommandPhase).getPokemon();
if (pokemon.summonData.turnCount <= 1) {
if (pokemon.tempSummonData.turnCount <= 1) {
this.setCursor(0);
} else {
this.setCursor(this.getCursor());

View File

@ -71,7 +71,7 @@ export default class TargetSelectUiHandler extends UiHandler {
*/
resetCursor(cursorN: number, user: Pokemon): void {
if (!isNullOrUndefined(cursorN)) {
if ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.summonData.waveTurnCount === 1) {
if ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.tempSummonData.waveTurnCount === 1) {
// Reset cursor on the first turn of a fight or if an ally was targeted last turn
cursorN = -1;
}