mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-23 15:59:26 +02:00
[Refactor] Clean up StatStageChangePhase
and PokemonPhase
Convert `const end` to method in `stat-stage-change-phase.ts` Remove unused method `aggregateStatStageChanges()` Rename `PokemonPhase.player` to `.isPlayer`
This commit is contained in:
parent
407cd65dcb
commit
92785b9a51
@ -2051,7 +2051,7 @@ export class StockpilingTag extends BattlerTag {
|
|||||||
super(BattlerTagType.STOCKPILING, BattlerTagLapseType.CUSTOM, 1, sourceMove);
|
super(BattlerTagType.STOCKPILING, BattlerTagLapseType.CUSTOM, 1, sourceMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onStatStagesChanged: StatStageChangeCallback = (_, statsChanged, statChanges) => {
|
private onStatStagesChanged: StatStageChangeCallback = (statsChanged, statChanges, _target) => {
|
||||||
const defChange = statChanges[statsChanged.indexOf(Stat.DEF)] ?? 0;
|
const defChange = statChanges[statsChanged.indexOf(Stat.DEF)] ?? 0;
|
||||||
const spDefChange = statChanges[statsChanged.indexOf(Stat.SPDEF)] ?? 0;
|
const spDefChange = statChanges[statsChanged.indexOf(Stat.SPDEF)] ?? 0;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
|||||||
this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => {
|
this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => {
|
||||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex);
|
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.isPlayer && p.fieldIndex === this.fieldIndex);
|
||||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.SWITCH, this.fieldIndex, false, true));
|
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.SWITCH, this.fieldIndex, false, true));
|
||||||
this.end();
|
this.end();
|
||||||
}, () => {
|
}, () => {
|
||||||
|
@ -21,7 +21,7 @@ export class CommonAnimPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
const target = this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon();
|
const target = this.targetIndex !== undefined ? (this.isPlayer ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon();
|
||||||
new CommonBattleAnim(this.anim, this.getPokemon(), target).play(this.scene, false, () => {
|
new CommonBattleAnim(this.anim, this.getPokemon(), target).play(this.scene, false, () => {
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
|
@ -33,13 +33,13 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
if (!this.preventEndure) {
|
if (!this.preventEndure) {
|
||||||
const instantReviveModifier = this.scene.applyModifier(PokemonInstantReviveModifier, this.player, this.getPokemon()) as PokemonInstantReviveModifier;
|
const instantReviveModifier = this.scene.applyModifier(PokemonInstantReviveModifier, this.isPlayer, this.getPokemon()) as PokemonInstantReviveModifier;
|
||||||
|
|
||||||
if (instantReviveModifier) {
|
if (instantReviveModifier) {
|
||||||
if (!--instantReviveModifier.stackCount) {
|
if (!--instantReviveModifier.stackCount) {
|
||||||
this.scene.removeModifier(instantReviveModifier);
|
this.scene.removeModifier(instantReviveModifier);
|
||||||
}
|
}
|
||||||
this.scene.updateModifiers(this.player);
|
this.scene.updateModifiers(this.isPlayer);
|
||||||
return this.end();
|
return this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.player) {
|
if (this.isPlayer) {
|
||||||
/** The total number of Pokemon in the player's party that can legally fight */
|
/** The total number of Pokemon in the player's party that can legally fight */
|
||||||
const legalPlayerPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
const legalPlayerPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
||||||
/** The total number of legal player Pokemon that aren't currently on the field */
|
/** The total number of legal player Pokemon that aren't currently on the field */
|
||||||
@ -159,7 +159,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
tryOverrideForBattleSpec(): boolean {
|
tryOverrideForBattleSpec(): boolean {
|
||||||
switch (this.scene.currentBattle.battleSpec) {
|
switch (this.scene.currentBattle.battleSpec) {
|
||||||
case BattleSpec.FINAL_BOSS:
|
case BattleSpec.FINAL_BOSS:
|
||||||
if (!this.player) {
|
if (!this.isPlayer) {
|
||||||
const enemy = this.getPokemon();
|
const enemy = this.getPokemon();
|
||||||
if (enemy.formIndex) {
|
if (enemy.formIndex) {
|
||||||
this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].secondStageWin, enemy.species.name, null, () => this.doFaint());
|
this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].secondStageWin, enemy.species.name, null, () => this.doFaint());
|
||||||
|
@ -290,7 +290,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* steal an item from the target granted by Grip Claw
|
* steal an item from the target granted by Grip Claw
|
||||||
*/
|
*/
|
||||||
if (this.move.getMove() instanceof AttackMove) {
|
if (this.move.getMove() instanceof AttackMove) {
|
||||||
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.isPlayer, user, target);
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@ -360,7 +360,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// If there are multiple hits, or if there are hits of the multi-hit move left
|
// If there are multiple hits, or if there are hits of the multi-hit move left
|
||||||
this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal }));
|
this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal }));
|
||||||
}
|
}
|
||||||
this.scene.applyModifiers(HitHealModifier, this.player, user);
|
this.scene.applyModifiers(HitHealModifier, this.isPlayer, user);
|
||||||
// Clear all cached move effectiveness values among targets
|
// Clear all cached move effectiveness values among targets
|
||||||
this.getTargets().forEach((target) => target.turnData.moveEffectiveness = null);
|
this.getTargets().forEach((target) => target.turnData.moveEffectiveness = null);
|
||||||
}
|
}
|
||||||
@ -429,7 +429,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||||
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
||||||
}
|
}
|
||||||
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
return (this.isPlayer ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */
|
/** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */
|
||||||
|
@ -61,7 +61,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
|
|||||||
} else if (healOrDamage) {
|
} else if (healOrDamage) {
|
||||||
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
|
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
|
||||||
if (!this.revive) {
|
if (!this.revive) {
|
||||||
this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
|
this.scene.applyModifiers(HealingBoosterModifier, this.isPlayer, hpRestoreMultiplier);
|
||||||
}
|
}
|
||||||
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
|
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
|
||||||
if (healAmount.value < 0) {
|
if (healAmount.value < 0) {
|
||||||
|
@ -1,29 +1,33 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
import Pokemon from "#app/field/pokemon";
|
import Pokemon from "#app/field/pokemon";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "#app/phases/field-phase";
|
||||||
|
|
||||||
export abstract class PokemonPhase extends FieldPhase {
|
export abstract class PokemonPhase extends FieldPhase {
|
||||||
protected battlerIndex: BattlerIndex | integer;
|
protected battlerIndex: BattlerIndex | number;
|
||||||
public player: boolean;
|
public isPlayer: boolean;
|
||||||
public fieldIndex: integer;
|
public fieldIndex: number;
|
||||||
|
|
||||||
constructor(scene: BattleScene, battlerIndex?: BattlerIndex | integer) {
|
constructor(scene: BattleScene, battlerIndex?: BattlerIndex | number) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
if (battlerIndex === undefined) {
|
if (battlerIndex === undefined) {
|
||||||
battlerIndex = scene.getField().find(p => p?.isActive())!.getBattlerIndex(); // TODO: is the bang correct here?
|
battlerIndex = scene.getField().find(p => p?.isActive())?.getBattlerIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.battlerIndex = battlerIndex;
|
if (battlerIndex !== undefined) {
|
||||||
this.player = battlerIndex < 2;
|
this.battlerIndex = battlerIndex;
|
||||||
this.fieldIndex = battlerIndex % 2;
|
this.isPlayer = battlerIndex < 2;
|
||||||
|
this.fieldIndex = battlerIndex % 2;
|
||||||
|
} else {
|
||||||
|
console.warn("Unable to find pokemon battlerIndex!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPokemon(): Pokemon {
|
getPokemon(): Pokemon {
|
||||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||||
return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct?
|
return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct?
|
||||||
}
|
}
|
||||||
return this.scene.getField()[this.battlerIndex]!; //TODO: is this bang correct?
|
return this.scene.getField()[this.battlerIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,64 +1,64 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { applyAbAttrs, applyPostStatStageChangeAbAttrs, applyPreStatStageChangeAbAttrs, PostStatStageChangeAbAttr, ProtectStatAbAttr, StatStageChangeCopyAbAttr, StatStageChangeMultiplierAbAttr } from "#app/data/ability";
|
import {
|
||||||
|
applyAbAttrs, applyPostStatStageChangeAbAttrs, applyPreStatStageChangeAbAttrs,
|
||||||
|
PostStatStageChangeAbAttr, ProtectStatAbAttr, StatStageChangeCopyAbAttr, StatStageChangeMultiplierAbAttr
|
||||||
|
} from "#app/data/ability";
|
||||||
import { ArenaTagSide, MistTag } from "#app/data/arena-tag";
|
import { ArenaTagSide, MistTag } from "#app/data/arena-tag";
|
||||||
import Pokemon from "#app/field/pokemon";
|
import Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
||||||
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
import { NumberHolder, BooleanHolder } from "#app/utils";
|
import { BooleanHolder, NumberHolder } from "#app/utils";
|
||||||
|
import { getStatKey, getStatStageChangeDescriptionKey, Stat, type BattleStat } from "#enums/stat";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
|
||||||
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
|
||||||
|
|
||||||
export type StatStageChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void;
|
export type StatStageChangeCallback = (changed: BattleStat[], relativeChanges: number[], target?: Pokemon) => void;
|
||||||
|
|
||||||
export class StatStageChangePhase extends PokemonPhase {
|
export class StatStageChangePhase extends PokemonPhase {
|
||||||
private stats: BattleStat[];
|
private filteredStats: BattleStat[];
|
||||||
private selfTarget: boolean;
|
private stagesHolder: NumberHolder;
|
||||||
private stages: integer;
|
private relLevels: number[];
|
||||||
private showMessage: boolean;
|
|
||||||
private ignoreAbilities: boolean;
|
|
||||||
private canBeCopied: boolean;
|
|
||||||
private onChange: StatStageChangeCallback | null;
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], stages: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatStageChangeCallback | null = null) {
|
scene: BattleScene,
|
||||||
|
battlerIndex: BattlerIndex,
|
||||||
|
protected selfTarget: boolean,
|
||||||
|
protected stats: BattleStat[],
|
||||||
|
protected stages: number,
|
||||||
|
protected showMessage: boolean = true,
|
||||||
|
protected ignoreAbilities: boolean = false,
|
||||||
|
protected canBeCopied: boolean = true,
|
||||||
|
protected onChange?: StatStageChangeCallback
|
||||||
|
) {
|
||||||
super(scene, battlerIndex);
|
super(scene, battlerIndex);
|
||||||
|
|
||||||
this.selfTarget = selfTarget;
|
|
||||||
this.stats = stats;
|
|
||||||
this.stages = stages;
|
|
||||||
this.showMessage = showMessage;
|
|
||||||
this.ignoreAbilities = ignoreAbilities;
|
|
||||||
this.canBeCopied = canBeCopied;
|
|
||||||
this.onChange = onChange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
public override start(): void {
|
||||||
const pokemon = this.getPokemon();
|
const pokemon = this.getPokemon();
|
||||||
|
|
||||||
if (!pokemon.isActive(true)) {
|
if (!pokemon.isActive(true)) {
|
||||||
return this.end();
|
return super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const stages = new NumberHolder(this.stages);
|
this.stagesHolder = new NumberHolder(this.stages);
|
||||||
|
|
||||||
if (!this.ignoreAbilities) {
|
if (!this.ignoreAbilities) {
|
||||||
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, stages);
|
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, false, this.stagesHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
let simulate = false;
|
let simulate = false;
|
||||||
|
|
||||||
const filteredStats = this.stats.filter(stat => {
|
this.filteredStats = this.stats.filter(stat => {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (!this.selfTarget && stages.value < 0) {
|
if (!this.selfTarget && this.stagesHolder.value < 0) {
|
||||||
// TODO: Include simulate boolean when tag applications can be simulated
|
// TODO: Include simulate boolean when tag applications can be simulated
|
||||||
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cancelled.value && !this.selfTarget && stages.value < 0) {
|
if (!cancelled.value && !this.selfTarget && this.stagesHolder.value < 0) {
|
||||||
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate);
|
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, pokemon, stat, cancelled, simulate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,83 +70,35 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const relLevels = filteredStats.map(s => (stages.value >= 1 ? Math.min(pokemon.getStatStage(s) + stages.value, 6) : Math.max(pokemon.getStatStage(s) + stages.value, -6)) - pokemon.getStatStage(s));
|
this.relLevels = this.filteredStats.map(s =>
|
||||||
|
(this.stagesHolder.value >= 1
|
||||||
|
? Math.min(pokemon.getStatStage(s) + this.stagesHolder.value, 6)
|
||||||
|
: Math.max(pokemon.getStatStage(s) + this.stagesHolder.value, -6)
|
||||||
|
) - pokemon.getStatStage(s));
|
||||||
|
|
||||||
this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels);
|
if (this.onChange) {
|
||||||
|
this.onChange(this.filteredStats, this.relLevels, this.getPokemon());
|
||||||
|
}
|
||||||
|
|
||||||
const end = () => {
|
if (this.relLevels.filter(l => l).length && this.scene.moveAnimations) {
|
||||||
if (this.showMessage) {
|
|
||||||
const messages = this.getStatStageChangeMessages(filteredStats, stages.value, relLevels);
|
|
||||||
for (const message of messages) {
|
|
||||||
this.scene.queueMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const s of filteredStats) {
|
|
||||||
if (stages.value > 0 && pokemon.getStatStage(s) < 6) {
|
|
||||||
if (!pokemon.turnData) {
|
|
||||||
// Temporary fix for missing turn data struct on turn 1
|
|
||||||
pokemon.resetTurnData();
|
|
||||||
}
|
|
||||||
pokemon.turnData.statStagesIncreased = true;
|
|
||||||
} else if (stages.value < 0 && pokemon.getStatStage(s) > -6) {
|
|
||||||
if (!pokemon.turnData) {
|
|
||||||
// Temporary fix for missing turn data struct on turn 1
|
|
||||||
pokemon.resetTurnData();
|
|
||||||
}
|
|
||||||
pokemon.turnData.statStagesDecreased = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pokemon.setStatStage(s, pokemon.getStatStage(s) + stages.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stages.value > 0 && this.canBeCopied) {
|
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
|
||||||
applyAbAttrs(StatStageChangeCopyAbAttr, opponent, null, false, this.stats, stages.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyPostStatStageChangeAbAttrs(PostStatStageChangeAbAttr, pokemon, filteredStats, this.stages, this.selfTarget);
|
|
||||||
|
|
||||||
// Look for any other stat change phases; if this is the last one, do White Herb check
|
|
||||||
const existingPhase = this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex);
|
|
||||||
if (!(existingPhase instanceof StatStageChangePhase)) {
|
|
||||||
// Apply White Herb if needed
|
|
||||||
const whiteHerb = this.scene.applyModifier(ResetNegativeStatStageModifier, this.player, pokemon) as ResetNegativeStatStageModifier;
|
|
||||||
// If the White Herb was applied, consume it
|
|
||||||
if (whiteHerb) {
|
|
||||||
whiteHerb.stackCount--;
|
|
||||||
if (whiteHerb.stackCount <= 0) {
|
|
||||||
this.scene.removeModifier(whiteHerb);
|
|
||||||
}
|
|
||||||
this.scene.updateModifiers(this.player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pokemon.updateInfo();
|
|
||||||
|
|
||||||
handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end());
|
|
||||||
};
|
|
||||||
|
|
||||||
if (relLevels.filter(l => l).length && this.scene.moveAnimations) {
|
|
||||||
pokemon.enableMask();
|
pokemon.enableMask();
|
||||||
const pokemonMaskSprite = pokemon.maskSprite;
|
const pokemonMaskSprite = pokemon.maskSprite;
|
||||||
|
|
||||||
const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale;
|
const tileX = (this.isPlayer ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale;
|
||||||
const tileY = ((this.player ? 148 : 84) + (stages.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale;
|
const tileY = ((this.isPlayer ? 148 : 84) + (this.stagesHolder.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale;
|
||||||
const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale();
|
const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale();
|
||||||
const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale();
|
const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale();
|
||||||
|
|
||||||
// On increase, show the red sprite located at ATK
|
// On increase, show the red sprite located at ATK
|
||||||
// On decrease, show the blue sprite located at SPD
|
// On decrease, show the blue sprite located at SPD
|
||||||
const spriteColor = stages.value >= 1 ? Stat[Stat.ATK].toLowerCase() : Stat[Stat.SPD].toLowerCase();
|
const spriteColor = this.stagesHolder.value >= 1 ? Stat[Stat.ATK].toLowerCase() : Stat[Stat.SPD].toLowerCase();
|
||||||
const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor);
|
const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor);
|
||||||
statSprite.setPipeline(this.scene.fieldSpritePipeline);
|
statSprite.setPipeline(this.scene.fieldSpritePipeline);
|
||||||
statSprite.setAlpha(0);
|
statSprite.setAlpha(0);
|
||||||
statSprite.setScale(6);
|
statSprite.setScale(6);
|
||||||
statSprite.setOrigin(0.5, 1);
|
statSprite.setOrigin(0.5, 1);
|
||||||
|
|
||||||
this.scene.playSound(`se/stat_${stages.value >= 1 ? "up" : "down"}`);
|
this.scene.playSound(`se/stat_${this.stagesHolder.value >= 1 ? "up" : "down"}`);
|
||||||
|
|
||||||
statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined));
|
statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined));
|
||||||
|
|
||||||
@ -167,44 +119,74 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: statSprite,
|
targets: statSprite,
|
||||||
duration: 1500,
|
duration: 1500,
|
||||||
y: `${stages.value >= 1 ? "-" : "+"}=${160 * 6}`
|
y: `${this.stagesHolder.value >= 1 ? "-" : "+"}=${160 * 6}`
|
||||||
});
|
});
|
||||||
|
|
||||||
this.scene.time.delayedCall(1750, () => {
|
this.scene.time.delayedCall(1750, () => {
|
||||||
pokemon.disableMask();
|
pokemon.disableMask();
|
||||||
end();
|
this.end();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
end();
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregateStatStageChanges(): void {
|
public override end(): void {
|
||||||
const accEva: BattleStat[] = [ Stat.ACC, Stat.EVA ];
|
const pokemon = this.getPokemon();
|
||||||
const isAccEva = accEva.some(s => this.stats.includes(s));
|
if (this.showMessage) {
|
||||||
let existingPhase: StatStageChangePhase;
|
const messages = this.getStatStageChangeMessages(this.filteredStats, this.stagesHolder.value, this.relLevels);
|
||||||
if (this.stats.length === 1) {
|
for (const message of messages) {
|
||||||
while ((existingPhase = (this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex && p.stats.length === 1
|
this.scene.queueMessage(message);
|
||||||
&& (p.stats[0] === this.stats[0])
|
}
|
||||||
&& p.selfTarget === this.selfTarget && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatStageChangePhase))) {
|
}
|
||||||
this.stages += existingPhase.stages;
|
|
||||||
|
|
||||||
if (!this.scene.tryRemovePhase(p => p === existingPhase)) {
|
for (const s of this.filteredStats) {
|
||||||
break;
|
if (this.stagesHolder.value > 0 && pokemon.getStatStage(s) < 6) {
|
||||||
|
if (!pokemon.turnData) {
|
||||||
|
// Temporary fix for missing turn data struct on turn 1
|
||||||
|
pokemon.resetTurnData();
|
||||||
}
|
}
|
||||||
|
pokemon.turnData.statStagesIncreased = true;
|
||||||
|
} else if (this.stagesHolder.value < 0 && pokemon.getStatStage(s) > -6) {
|
||||||
|
if (!pokemon.turnData) {
|
||||||
|
// Temporary fix for missing turn data struct on turn 1
|
||||||
|
pokemon.resetTurnData();
|
||||||
|
}
|
||||||
|
pokemon.turnData.statStagesDecreased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.setStatStage(s, pokemon.getStatStage(s) + this.stagesHolder.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stagesHolder.value > 0 && this.canBeCopied) {
|
||||||
|
for (const opponent of pokemon.getOpponents()) {
|
||||||
|
applyAbAttrs(StatStageChangeCopyAbAttr, opponent, null, false, this.stats, this.stagesHolder.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while ((existingPhase = (this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget
|
|
||||||
&& (accEva.some(s => p.stats.includes(s)) === isAccEva)
|
applyPostStatStageChangeAbAttrs(PostStatStageChangeAbAttr, pokemon, this.filteredStats, this.stages, this.selfTarget);
|
||||||
&& p.stages === this.stages && p.showMessage === this.showMessage && p.ignoreAbilities === this.ignoreAbilities) as StatStageChangePhase))) {
|
|
||||||
this.stats.push(...existingPhase.stats);
|
// Look for any other stat change phases; if this is the last one, do White Herb check
|
||||||
if (!this.scene.tryRemovePhase(p => p === existingPhase)) {
|
const existingPhase = this.scene.findPhase(p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex);
|
||||||
break;
|
if (!(existingPhase instanceof StatStageChangePhase)) {
|
||||||
|
// Apply White Herb if needed
|
||||||
|
const whiteHerb = this.scene.applyModifier(ResetNegativeStatStageModifier, this.isPlayer, pokemon) as ResetNegativeStatStageModifier;
|
||||||
|
// If the White Herb was applied, consume it
|
||||||
|
if (whiteHerb) {
|
||||||
|
whiteHerb.stackCount--;
|
||||||
|
if (whiteHerb.stackCount <= 0) {
|
||||||
|
this.scene.removeModifier(whiteHerb);
|
||||||
|
}
|
||||||
|
this.scene.updateModifiers(this.isPlayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pokemon.updateInfo();
|
||||||
|
|
||||||
|
handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatStageChangeMessages(stats: BattleStat[], stages: integer, relStages: integer[]): string[] {
|
protected getStatStageChangeMessages(stats: BattleStat[], stages: number, relStages: number[]): string[] {
|
||||||
const messages: string[] = [];
|
const messages: string[] = [];
|
||||||
|
|
||||||
const relStageStatIndexes = {};
|
const relStageStatIndexes = {};
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
import Pokemon, { PokemonSummonData } from "#app/field/pokemon";
|
|
||||||
import { StockpilingTag } from "#app/data/battler-tags";
|
import { StockpilingTag } from "#app/data/battler-tags";
|
||||||
import { Stat } from "#enums/stat";
|
import Pokemon, { PokemonSummonData } from "#app/field/pokemon";
|
||||||
import * as messages from "#app/messages";
|
import * as messages from "#app/messages";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.spyOn(messages, "getPokemonNameWithAffix").mockImplementation(() => "");
|
vi.spyOn(messages, "getPokemonNameWithAffix").mockImplementation(() => "");
|
||||||
@ -27,7 +27,7 @@ describe("BattlerTag - StockpilingTag", () => {
|
|||||||
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
||||||
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||||
|
|
||||||
(phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [ 1, 1 ]);
|
(phase as StatStageChangePhase)["onChange"]!([ Stat.DEF, Stat.SPDEF ], [ 1, 1 ], mockPokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
subject.onAdd(mockPokemon);
|
subject.onAdd(mockPokemon);
|
||||||
@ -54,7 +54,7 @@ describe("BattlerTag - StockpilingTag", () => {
|
|||||||
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
||||||
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||||
|
|
||||||
(phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [ 1, 1 ]);
|
(phase as StatStageChangePhase)["onChange"]!([ Stat.DEF, Stat.SPDEF ], [ 1, 1 ], mockPokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
subject.onAdd(mockPokemon);
|
subject.onAdd(mockPokemon);
|
||||||
@ -79,7 +79,7 @@ describe("BattlerTag - StockpilingTag", () => {
|
|||||||
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
expect((phase as StatStageChangePhase)["stages"]).toEqual(1);
|
||||||
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||||
|
|
||||||
(phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [ 1, 1 ]);
|
(phase as StatStageChangePhase)["onChange"]!([ Stat.DEF, Stat.SPDEF ], [ 1, 1 ], mockPokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
subject.onOverlap(mockPokemon);
|
subject.onOverlap(mockPokemon);
|
||||||
@ -109,7 +109,7 @@ describe("BattlerTag - StockpilingTag", () => {
|
|||||||
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||||
|
|
||||||
// def doesn't change
|
// def doesn't change
|
||||||
(phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [ 1 ]);
|
(phase as StatStageChangePhase)["onChange"]!([ Stat.SPDEF ], [ 1 ], mockPokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
subject.onAdd(mockPokemon);
|
subject.onAdd(mockPokemon);
|
||||||
@ -121,7 +121,7 @@ describe("BattlerTag - StockpilingTag", () => {
|
|||||||
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||||
|
|
||||||
// def doesn't change
|
// def doesn't change
|
||||||
(phase as StatStageChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [ 1 ]);
|
(phase as StatStageChangePhase)["onChange"]!([ Stat.SPDEF ], [ 1 ], mockPokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
subject.onOverlap(mockPokemon);
|
subject.onOverlap(mockPokemon);
|
||||||
|
Loading…
Reference in New Issue
Block a user