mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-06 08:22:16 +02:00
Remove BattleStat
, Use "Stat Stages" & New Names
This commit is contained in:
parent
8461ca3fc2
commit
db9925ec0f
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ import { MoveEffectPhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase }
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { BlockNonDirectDamageAbAttr, ProtectStatAbAttr, applyAbAttrs } from "./ability";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CommonAnim, CommonBattleAnim } from "./battle-anims";
|
||||
import i18next from "i18next";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -726,7 +726,7 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
if (!cancelled.value) {
|
||||
pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
|
||||
const statLevels = new Utils.NumberHolder(-1);
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [BattleStat.SPD], statLevels.value));
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], statLevels.value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -814,7 +814,7 @@ class TailwindTag extends ArenaTag {
|
||||
// Raise attack by one stage if party member has WIND_RIDER ability
|
||||
if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
|
||||
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex()));
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK], 1, true));
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
import i18next, { ParseKeys } from "i18next";
|
||||
|
||||
export enum BattleStat {
|
||||
ATK,
|
||||
DEF,
|
||||
SPATK,
|
||||
SPDEF,
|
||||
SPD,
|
||||
ACC,
|
||||
EVA,
|
||||
RAND,
|
||||
HP
|
||||
}
|
||||
|
||||
export function getBattleStatName(stat: BattleStat) {
|
||||
switch (stat) {
|
||||
case BattleStat.ATK:
|
||||
return i18next.t("pokemonInfo:Stat.ATK");
|
||||
case BattleStat.DEF:
|
||||
return i18next.t("pokemonInfo:Stat.DEF");
|
||||
case BattleStat.SPATK:
|
||||
return i18next.t("pokemonInfo:Stat.SPATK");
|
||||
case BattleStat.SPDEF:
|
||||
return i18next.t("pokemonInfo:Stat.SPDEF");
|
||||
case BattleStat.SPD:
|
||||
return i18next.t("pokemonInfo:Stat.SPD");
|
||||
case BattleStat.ACC:
|
||||
return i18next.t("pokemonInfo:Stat.ACC");
|
||||
case BattleStat.EVA:
|
||||
return i18next.t("pokemonInfo:Stat.EVA");
|
||||
case BattleStat.HP:
|
||||
return i18next.t("pokemonInfo:Stat.HPStat");
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: BattleStat
|
||||
export function getBattleStatLevelChangeDescription(pokemonNameWithAffix: string, stats: string, levels: integer, up: boolean, count: number = 1) {
|
||||
const stringKey = (() => {
|
||||
if (up) {
|
||||
switch (levels) {
|
||||
case 1:
|
||||
return "battle:statRose";
|
||||
case 2:
|
||||
return "battle:statSharplyRose";
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
return "battle:statRoseDrastically";
|
||||
default:
|
||||
return "battle:statWontGoAnyHigher";
|
||||
}
|
||||
} else {
|
||||
switch (levels) {
|
||||
case 1:
|
||||
return "battle:statFell";
|
||||
case 2:
|
||||
return "battle:statHarshlyFell";
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
return "battle:statSeverelyFell";
|
||||
default:
|
||||
return "battle:statWontGoAnyLower";
|
||||
}
|
||||
}
|
||||
})();
|
||||
return i18next.t(stringKey as ParseKeys, { pokemonNameWithAffix, stats, count });
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "./battle-anims";
|
||||
import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangeCallback, StatChangePhase } from "../phases";
|
||||
import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangeCallback, StatStageChangePhase } from "../phases";
|
||||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import Pokemon, { MoveResult, HitResult } from "../field/pokemon";
|
||||
import { Stat, getStatName } from "./pokemon-stat";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import * as Utils from "../utils";
|
||||
import { ChargeAttr, MoveFlags, allMoves } from "./move";
|
||||
@ -10,7 +9,6 @@ import { Type } from "./type";
|
||||
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability";
|
||||
import { TerrainType } from "./terrain";
|
||||
import { WeatherType } from "./weather";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { allAbilities } from "./ability";
|
||||
import { SpeciesFormChangeManualTrigger } from "./pokemon-forms";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -18,6 +16,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import i18next from "#app/plugins/i18n.js";
|
||||
import { Stat, BattleStat, EFFECTIVE_STATS, EffectiveStat, getStatKey } from "#app/enums/stat";
|
||||
|
||||
export enum BattlerTagLapseType {
|
||||
FAINT,
|
||||
@ -296,8 +295,8 @@ export class ConfusedTag extends BattlerTag {
|
||||
|
||||
// 1/3 chance of hitting self with a 40 base power move
|
||||
if (pokemon.randSeedInt(3) === 0) {
|
||||
const atk = pokemon.getBattleStat(Stat.ATK);
|
||||
const def = pokemon.getBattleStat(Stat.DEF);
|
||||
const atk = pokemon.getEffectiveStat(Stat.ATK);
|
||||
const def = pokemon.getEffectiveStat(Stat.DEF);
|
||||
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
|
||||
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsConfusedLapseHurtItself"));
|
||||
pokemon.damageAndUpdate(damage);
|
||||
@ -701,7 +700,7 @@ export class OctolockTag extends TrappedTag {
|
||||
const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
if (shouldLapse) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF, BattleStat.SPDEF], -1));
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], -1));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1027,7 +1026,7 @@ export class ContactDamageProtectedTag extends ProtectedTag {
|
||||
}
|
||||
}
|
||||
|
||||
export class ContactStatChangeProtectedTag extends ProtectedTag {
|
||||
export class ContactStatStageChangeProtectedTag extends ProtectedTag {
|
||||
private stat: BattleStat;
|
||||
private levels: number;
|
||||
|
||||
@ -1044,7 +1043,7 @@ export class ContactStatChangeProtectedTag extends ProtectedTag {
|
||||
*/
|
||||
loadTag(source: BattlerTag | any): void {
|
||||
super.loadTag(source);
|
||||
this.stat = source.stat as BattleStat;
|
||||
this.stat = source.stat;
|
||||
this.levels = source.levels;
|
||||
}
|
||||
|
||||
@ -1055,7 +1054,7 @@ export class ContactStatChangeProtectedTag extends ProtectedTag {
|
||||
const effectPhase = pokemon.scene.getCurrentPhase();
|
||||
if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) {
|
||||
const attacker = effectPhase.getPokemon();
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels));
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,11 +1281,10 @@ export class HighestStatBoostTag extends AbilityBattlerTag {
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
const stats = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
|
||||
let highestStat: Stat;
|
||||
stats.map(s => pokemon.getBattleStat(s)).reduce((highestValue: number, value: number, i: number) => {
|
||||
let highestStat: EffectiveStat;
|
||||
EFFECTIVE_STATS.map(s => pokemon.getEffectiveStat(s)).reduce((highestValue: number, value: number, i: number) => {
|
||||
if (value > highestValue) {
|
||||
highestStat = stats[i];
|
||||
highestStat = EFFECTIVE_STATS[i];
|
||||
return value;
|
||||
}
|
||||
return highestValue;
|
||||
@ -1304,7 +1302,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag {
|
||||
break;
|
||||
}
|
||||
|
||||
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsHighestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), statName: getStatName(highestStat) }), null, false, null, true);
|
||||
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsHighestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), statName: getStatKey(highestStat) }), null, false, null, true);
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
@ -1622,9 +1620,9 @@ export class IceFaceTag extends BattlerTag {
|
||||
*/
|
||||
export class StockpilingTag extends BattlerTag {
|
||||
public stockpiledCount: number = 0;
|
||||
public statChangeCounts: { [BattleStat.DEF]: number; [BattleStat.SPDEF]: number } = {
|
||||
[BattleStat.DEF]: 0,
|
||||
[BattleStat.SPDEF]: 0
|
||||
public statChangeCounts: { [Stat.DEF]: number; [Stat.SPDEF]: number } = {
|
||||
[Stat.DEF]: 0,
|
||||
[Stat.SPDEF]: 0
|
||||
};
|
||||
|
||||
constructor(sourceMove: Moves = Moves.NONE) {
|
||||
@ -1632,15 +1630,15 @@ export class StockpilingTag extends BattlerTag {
|
||||
}
|
||||
|
||||
private onStatsChanged: StatChangeCallback = (_, statsChanged, statChanges) => {
|
||||
const defChange = statChanges[statsChanged.indexOf(BattleStat.DEF)] ?? 0;
|
||||
const spDefChange = statChanges[statsChanged.indexOf(BattleStat.SPDEF)] ?? 0;
|
||||
const defChange = statChanges[statsChanged.indexOf(Stat.DEF)] ?? 0;
|
||||
const spDefChange = statChanges[statsChanged.indexOf(Stat.SPDEF)] ?? 0;
|
||||
|
||||
if (defChange) {
|
||||
this.statChangeCounts[BattleStat.DEF]++;
|
||||
this.statChangeCounts[Stat.DEF]++;
|
||||
}
|
||||
|
||||
if (spDefChange) {
|
||||
this.statChangeCounts[BattleStat.SPDEF]++;
|
||||
this.statChangeCounts[Stat.SPDEF]++;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1648,8 +1646,8 @@ export class StockpilingTag extends BattlerTag {
|
||||
super.loadTag(source);
|
||||
this.stockpiledCount = source.stockpiledCount || 0;
|
||||
this.statChangeCounts = {
|
||||
[ BattleStat.DEF ]: source.statChangeCounts?.[ BattleStat.DEF ] ?? 0,
|
||||
[ BattleStat.SPDEF ]: source.statChangeCounts?.[ BattleStat.SPDEF ] ?? 0,
|
||||
[ Stat.DEF ]: source.statChangeCounts?.[ Stat.DEF ] ?? 0,
|
||||
[ Stat.SPDEF ]: source.statChangeCounts?.[ Stat.SPDEF ] ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1669,9 +1667,9 @@ export class StockpilingTag extends BattlerTag {
|
||||
}));
|
||||
|
||||
// Attempt to increase DEF and SPDEF by one stage, keeping track of successful changes.
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(
|
||||
pokemon.scene, pokemon.getBattlerIndex(), true,
|
||||
[BattleStat.SPDEF, BattleStat.DEF], 1, true, false, true, this.onStatsChanged
|
||||
[Stat.SPDEF, Stat.DEF], 1, true, false, true, this.onStatsChanged
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1685,15 +1683,15 @@ export class StockpilingTag extends BattlerTag {
|
||||
* one stage for each stack which had successfully changed that particular stat during onAdd.
|
||||
*/
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
const defChange = this.statChangeCounts[BattleStat.DEF];
|
||||
const spDefChange = this.statChangeCounts[BattleStat.SPDEF];
|
||||
const defChange = this.statChangeCounts[Stat.DEF];
|
||||
const spDefChange = this.statChangeCounts[Stat.SPDEF];
|
||||
|
||||
if (defChange) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF], -defChange, true, false, true));
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF ], -defChange, true, false, true));
|
||||
}
|
||||
|
||||
if (spDefChange) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.SPDEF], -spDefChange, true, false, true));
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF ], -spDefChange, true, false, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1831,11 +1829,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
||||
case BattlerTagType.SPIKY_SHIELD:
|
||||
return new ContactDamageProtectedTag(sourceMove, 8);
|
||||
case BattlerTagType.KINGS_SHIELD:
|
||||
return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.ATK, -1);
|
||||
return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.ATK, -1);
|
||||
case BattlerTagType.OBSTRUCT:
|
||||
return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.DEF, -2);
|
||||
return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.DEF, -2);
|
||||
case BattlerTagType.SILK_TRAP:
|
||||
return new ContactStatChangeProtectedTag(sourceMove, tagType, BattleStat.SPD, -1);
|
||||
return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.SPD, -1);
|
||||
case BattlerTagType.BANEFUL_BUNKER:
|
||||
return new ContactPoisonProtectedTag(sourceMove);
|
||||
case BattlerTagType.BURNING_BULWARK:
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { PokemonHealPhase, StatChangePhase } from "../phases";
|
||||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import Pokemon, { HitResult } from "../field/pokemon";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import { getStatusEffectHealText } from "./status-effect";
|
||||
import * as Utils from "../utils";
|
||||
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./ability";
|
||||
import i18next from "i18next";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { BattleStat, Stat } from "#app/enums/stat";
|
||||
|
||||
export function getBerryName(berryType: BerryType): string {
|
||||
return i18next.t(`berry:${BerryType[berryType]}.name`);
|
||||
@ -34,9 +34,10 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||
case BerryType.SALAC:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new Utils.NumberHolder(0.25);
|
||||
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold);
|
||||
return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6;
|
||||
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
return (pokemon: Pokemon) => {
|
||||
@ -94,10 +95,11 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
if (pokemon.battleData) {
|
||||
pokemon.battleData.berriesEaten.push(berryType);
|
||||
}
|
||||
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||
const statLevels = new Utils.NumberHolder(1);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value));
|
||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
const statStages = new Utils.NumberHolder(1);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statStages);
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value));
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
return (pokemon: Pokemon) => {
|
||||
@ -111,9 +113,10 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
if (pokemon.battleData) {
|
||||
pokemon.battleData.berriesEaten.push(berryType);
|
||||
}
|
||||
const randStat = Utils.randSeedInt(Stat.EVA, Stat.ATK);
|
||||
const statLevels = new Utils.NumberHolder(2);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels);
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value));
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], statLevels.value)); // TODO: BattleStats
|
||||
};
|
||||
case BerryType.LEPPA:
|
||||
return (pokemon: Pokemon) => {
|
||||
|
576
src/data/move.ts
576
src/data/move.ts
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,9 @@
|
||||
import { Stat, getStatName } from "./pokemon-stat";
|
||||
import * as Utils from "../utils";
|
||||
import { TextStyle, getBBCodeFrag } from "../ui/text";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { UiTheme } from "#enums/ui-theme";
|
||||
import i18next from "i18next";
|
||||
import { Stat, EFFECTIVE_STATS, getShortenedStatKey } from "#app/enums/stat.js";
|
||||
|
||||
export { Nature };
|
||||
|
||||
@ -14,10 +14,9 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals
|
||||
ret = i18next.t("nature:" + ret as any);
|
||||
}
|
||||
if (includeStatEffects) {
|
||||
const stats = Utils.getEnumValues(Stat).slice(1);
|
||||
let increasedStat: Stat | null = null;
|
||||
let decreasedStat: Stat | null = null;
|
||||
for (const stat of stats) {
|
||||
for (const stat of EFFECTIVE_STATS) {
|
||||
const multiplier = getNatureStatMultiplier(nature, stat);
|
||||
if (multiplier > 1) {
|
||||
increasedStat = stat;
|
||||
@ -28,7 +27,7 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals
|
||||
const textStyle = forStarterSelect ? TextStyle.SUMMARY_ALT : TextStyle.WINDOW;
|
||||
const getTextFrag = !ignoreBBCode ? (text: string, style: TextStyle) => getBBCodeFrag(text, style, uiTheme) : (text: string, style: TextStyle) => text;
|
||||
if (increasedStat && decreasedStat) {
|
||||
ret = `${getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(`, textStyle)}${getTextFrag(`+${getStatName(increasedStat, true)}`, TextStyle.SUMMARY_PINK)}${getTextFrag("/", textStyle)}${getTextFrag(`-${getStatName(decreasedStat, true)}`, TextStyle.SUMMARY_BLUE)}${getTextFrag(")", textStyle)}`;
|
||||
ret = `${getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(`, textStyle)}${getTextFrag(`+${i18next.t(getShortenedStatKey(increasedStat))}`, TextStyle.SUMMARY_PINK)}${getTextFrag("/", textStyle)}${getTextFrag(`-${getShortenedStatKey(decreasedStat)}`, TextStyle.SUMMARY_BLUE)}${getTextFrag(")", textStyle)}`;
|
||||
} else {
|
||||
ret = getTextFrag(`${ret}${!forStarterSelect ? "\n" : " "}(-)`, textStyle);
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { Stat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
export { Stat };
|
||||
|
||||
export function getStatName(stat: Stat, shorten: boolean = false) {
|
||||
let ret: string = "";
|
||||
switch (stat) {
|
||||
case Stat.HP:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.HP") : i18next.t("pokemonInfo:Stat.HPshortened");
|
||||
break;
|
||||
case Stat.ATK:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.ATK") : i18next.t("pokemonInfo:Stat.ATKshortened");
|
||||
break;
|
||||
case Stat.DEF:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.DEF") : i18next.t("pokemonInfo:Stat.DEFshortened");
|
||||
break;
|
||||
case Stat.SPATK:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.SPATK") : i18next.t("pokemonInfo:Stat.SPATKshortened");
|
||||
break;
|
||||
case Stat.SPDEF:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.SPDEF") : i18next.t("pokemonInfo:Stat.SPDEFshortened");
|
||||
break;
|
||||
case Stat.SPD:
|
||||
ret = !shorten ? i18next.t("pokemonInfo:Stat.SPD") : i18next.t("pokemonInfo:Stat.SPDshortened");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -20,8 +20,30 @@ export enum Stat {
|
||||
export const PERMANENT_STATS = [ Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const;
|
||||
export type PermanentStat = typeof PERMANENT_STATS[number];
|
||||
|
||||
export const EFFECTIVE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ] as const;
|
||||
export type EffectiveStat = typeof EFFECTIVE_STATS[number];
|
||||
|
||||
export const BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC, Stat.EVA ] as const;
|
||||
export type BattleStat = typeof BATTLE_STATS[number];
|
||||
|
||||
export const TEMP_BATTLE_STATS = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD, Stat.ACC ] as const;
|
||||
export type TempBattleStat = typeof TEMP_BATTLE_STATS[number];
|
||||
|
||||
export function getStatStageChangeDescriptionKey(stages: integer, isIncrease: boolean) {
|
||||
if (stages === 1) {
|
||||
return isIncrease ? "battle:statRose" : "battle:statFell";
|
||||
} else if (stages === 2) {
|
||||
return isIncrease ? "battle:statSharplyRose" : "battle:statHarshlyFell";
|
||||
} else if (stages <= 6) {
|
||||
return isIncrease ? "battle:statRoseDrastically" : "battle:statSeverelyFell";
|
||||
}
|
||||
return isIncrease ? "battle:statWontGoAnyHigher" : "battle:statWontGoAnyLower";
|
||||
}
|
||||
|
||||
export function getStatKey(stat: Stat) {
|
||||
return `pokemonInfo:Stat.${Stat[stat]}`;
|
||||
}
|
||||
|
||||
export function getShortenedStatKey(stat: PermanentStat) {
|
||||
return `pokemonInfo:Stat.${Stat[stat]}shortened`;
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ import BattleScene, { AnySound } from "../battle-scene";
|
||||
import { Variant, VariantSet, variantColorCache } from "#app/data/variant";
|
||||
import { variantData } from "#app/data/variant";
|
||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, StatusMoveTypeImmunityAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags, NeutralDamageAgainstFlyingTypeMultiplierAttr, OneHitKOAccuracyAttr } from "../data/move";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, StatusMoveTypeImmunityAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags, NeutralDamageAgainstFlyingTypeMultiplierAttr, OneHitKOAccuracyAttr } from "../data/move";
|
||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||
import { Constructor } from "#app/utils";
|
||||
import * as Utils from "../utils";
|
||||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
||||
import { getLevelTotalExp } from "../data/exp";
|
||||
import { Stat } from "../data/pokemon-stat";
|
||||
import { BATTLE_STATS, EFFECTIVE_STATS, Stat } from "#enums/stat";
|
||||
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier } from "../modifier/modifier";
|
||||
import { PokeballType } from "../data/pokeball";
|
||||
import { Gender } from "../data/gender";
|
||||
@ -17,11 +17,11 @@ import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
|
||||
import { Status, StatusEffect, getRandomStatus } from "../data/status-effect";
|
||||
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions";
|
||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms";
|
||||
import { DamagePhase, FaintPhase, LearnMovePhase, MoveEffectPhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase, ToggleDoublePositionPhase, MoveEndPhase } from "../phases";
|
||||
import { DamagePhase, FaintPhase, LearnMovePhase, MoveEffectPhase, ObtainStatusEffectPhase, StatStageChangePhase, SwitchSummonPhase, ToggleDoublePositionPhase, MoveEndPhase } from "../phases";
|
||||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag } from "../data/battler-tags";
|
||||
import { WeatherType } from "../data/weather";
|
||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
|
||||
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr } from "../data/ability";
|
||||
import { Ability, AbAttr, StatStageMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStageChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatStageMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr } from "../data/ability";
|
||||
import PokemonData from "../system/pokemon-data";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { Mode } from "../ui/ui";
|
||||
@ -49,7 +49,7 @@ import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||
import { PermanentStat, BattleStat, PERMANENT_STATS } from "#app/enums/stat"; // TODO: Add type
|
||||
import { PermanentStat, BattleStat, PERMANENT_STATS, EffectiveStat } from "#app/enums/stat"; // TODO: Add type
|
||||
|
||||
export enum FieldPosition {
|
||||
CENTER,
|
||||
@ -735,7 +735,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
getBattleStat(stat: PermanentStat & BattleStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer {
|
||||
getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer {
|
||||
const statLevel = new Utils.IntegerHolder(this.getStatStage(stat));
|
||||
if (opponent) {
|
||||
if (isCritical) {
|
||||
@ -750,7 +750,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
break;
|
||||
}
|
||||
}
|
||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel);
|
||||
applyAbAttrs(IgnoreOpponentStatStageChangesAbAttr, opponent, null, statLevel);
|
||||
if (move) {
|
||||
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel);
|
||||
}
|
||||
@ -763,12 +763,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
const fieldApplied = new Utils.BooleanHolder(false);
|
||||
for (const pokemon of this.scene.getField(true)) {
|
||||
applyFieldBattleStatMultiplierAbAttrs(FieldMultiplyBattleStatAbAttr, pokemon, stat, statValue, this, fieldApplied);
|
||||
applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied);
|
||||
if (fieldApplied.value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, battleStat, statValue); // TODO: BattleStat
|
||||
applyStatStageMultiplierAbAttrs(StatStageMultiplierAbAttr, this, stat, statValue);
|
||||
let ret = statValue.value * (Math.max(2, 2 + statLevel.value) / Math.max(2, 2 - statLevel.value));
|
||||
switch (stat) {
|
||||
case Stat.ATK:
|
||||
@ -1374,7 +1374,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const types = this.getTypes(true);
|
||||
const enemyTypes = opponent.getTypes(true, true);
|
||||
/** Is this Pokemon faster than the opponent? */
|
||||
const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, opponent) : this.getStat(Stat.SPD)) >= opponent.getBattleStat(Stat.SPD, this);
|
||||
const outspeed = (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false)) >= opponent.getEffectiveStat(Stat.SPD, this);
|
||||
/**
|
||||
* Based on how effective this Pokemon's types are offensively against the opponent's types.
|
||||
* This score is increased by 25 percent if this Pokemon is faster than the opponent.
|
||||
@ -1730,7 +1730,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]);
|
||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttrOnHit) ? 0.5 : 1)]);
|
||||
// Trainers get a weight bump to stat buffing moves
|
||||
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => a.levels > 1 && a.selfTarget) ? 1.25 : 1)]);
|
||||
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatStageChangeAttr).some(a => a.stages > 1 && a.selfTarget) ? 1.25 : 1)]);
|
||||
// Trainers get a weight decrease to multiturn moves
|
||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(ChargeAttr) || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1)]);
|
||||
}
|
||||
@ -1950,8 +1950,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const userAccStage = new Utils.IntegerHolder(this.getStatStage(Stat.ACC));
|
||||
const targetEvaStage = new Utils.IntegerHolder(target.getStatStage(Stat.EVA));
|
||||
|
||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccStage);
|
||||
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, targetEvaStage);
|
||||
applyAbAttrs(IgnoreOpponentStatStageChangesAbAttr, target, null, userAccStage);
|
||||
applyAbAttrs(IgnoreOpponentStatStageChangesAbAttr, this, null, targetEvaStage);
|
||||
applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, targetEvaStage);
|
||||
applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvaStage);
|
||||
this.scene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
||||
@ -1967,10 +1967,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
: 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6));
|
||||
}
|
||||
|
||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, sourceMove); // TODO: BattleStat
|
||||
applyStatStageMultiplierAbAttrs(StatStageMultiplierAbAttr, this, Stat.ACC, accuracyMultiplier, sourceMove); // TODO: BattleStat
|
||||
|
||||
const evasionMultiplier = new Utils.NumberHolder(1);
|
||||
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier); // TODO: BattleStat
|
||||
applyStatStageMultiplierAbAttrs(StatStageMultiplierAbAttr, target, Stat.EVA, evasionMultiplier); // TODO: BattleStat
|
||||
|
||||
accuracyMultiplier.value /= evasionMultiplier.value;
|
||||
|
||||
@ -2087,8 +2087,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
isCritical = false;
|
||||
}
|
||||
}
|
||||
const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical));
|
||||
const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
|
||||
const sourceAtk = new Utils.IntegerHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical));
|
||||
const targetDef = new Utils.IntegerHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
|
||||
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
|
||||
applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier);
|
||||
const screenMultiplier = new Utils.NumberHolder(1);
|
||||
@ -2497,11 +2497,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @param source {@linkcode Pokemon} the pokemon whose stats/Tags are to be passed on from, ie: the Pokemon using Baton Pass
|
||||
*/
|
||||
transferSummon(source: Pokemon): void {
|
||||
/** TODO: BattleStats
|
||||
for (const stat of battleStats) {
|
||||
this.summonData.battleStats[stat] = source.summonData.battleStats[stat];
|
||||
// Copy all stat stages
|
||||
for (const s of BATTLE_STATS) {
|
||||
const sourceStage = source.getStatStage(s);
|
||||
if ((this instanceof PlayerPokemon) && (sourceStage === 6)) {
|
||||
this.scene.validateAchv(achvs.TRANSFER_MAX_STAT_STAGE);
|
||||
}
|
||||
*/
|
||||
this.setStatStage(s, sourceStage);
|
||||
}
|
||||
|
||||
for (const tag of source.summonData.tags) {
|
||||
|
||||
// bypass yawn, and infatuation as those can not be passed via Baton Pass
|
||||
@ -2511,11 +2515,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
this.summonData.tags.push(tag);
|
||||
}
|
||||
/** TODO: BattleStats
|
||||
if (this instanceof PlayerPokemon && source.summonData.battleStats.find(bs => bs === 6)) {
|
||||
this.scene.validateAchv(achvs.TRANSFER_MAX_BATTLE_STAT);
|
||||
}
|
||||
*/
|
||||
|
||||
this.updateInfo();
|
||||
}
|
||||
|
||||
@ -4161,45 +4161,46 @@ export class EnemyPokemon extends Pokemon {
|
||||
return true;
|
||||
}
|
||||
|
||||
handleBossSegmentCleared(segmentIndex: integer): void { // TODO: BattleStat
|
||||
handleBossSegmentCleared(segmentIndex: integer): void {
|
||||
while (segmentIndex - 1 < this.bossSegmentIndex) {
|
||||
let boostedStat = BattleStat.RAND;
|
||||
// Filter out already maxed out stat stages and weigh the rest based on existing stats
|
||||
const leftoverStats = EFFECTIVE_STATS.filter((s: EffectiveStat) => this.getStatStage(s) < 6);
|
||||
const statWeights = leftoverStats.map((s: EffectiveStat) => this.getStat(s, false));
|
||||
|
||||
const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3);
|
||||
const statWeights = new Array().fill(battleStats.length).filter((bs: BattleStat) => this.summonData.battleStats[bs] < 6).map((bs: BattleStat) => this.getStat(bs + 1));
|
||||
const statThresholds: integer[] = [];
|
||||
let boostedStat: EffectiveStat;
|
||||
const statThresholds: number[] = [];
|
||||
let totalWeight = 0;
|
||||
for (const bs of battleStats) {
|
||||
totalWeight += statWeights[bs];
|
||||
|
||||
for (const i in statWeights) {
|
||||
totalWeight += statWeights[i];
|
||||
statThresholds.push(totalWeight);
|
||||
}
|
||||
|
||||
// Pick a random stat from the leftover stats to increase its stages
|
||||
const randInt = Utils.randSeedInt(totalWeight);
|
||||
|
||||
for (const bs of battleStats) {
|
||||
if (randInt < statThresholds[bs]) {
|
||||
boostedStat = bs;
|
||||
for (const i in statThresholds) {
|
||||
if (randInt < statThresholds[i]) {
|
||||
boostedStat = leftoverStats[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let statLevels = 1;
|
||||
|
||||
// Increment the amount of stages the chosen stat will be raised
|
||||
let stages = 1;
|
||||
switch (segmentIndex) {
|
||||
case 1:
|
||||
if (this.bossSegments >= 3) {
|
||||
statLevels++;
|
||||
stages++;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this.bossSegments >= 5) {
|
||||
statLevels++;
|
||||
stages++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.scene.unshiftPhase(new StatChangePhase(this.scene, this.getBattlerIndex(), true, [ boostedStat ], statLevels, true, true));
|
||||
|
||||
this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.getBattlerIndex(), true, [ boostedStat! ], stages, true, true));
|
||||
this.bossSegmentIndex--;
|
||||
}
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Master League Champion",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Teamwork",
|
||||
description: "Baton pass to another party member with at least one stat maxed out",
|
||||
description: "Baton pass to another party member with at least one stat stage maxed out",
|
||||
},
|
||||
"MAX_FRIENDSHIP": {
|
||||
name: "Friendmaxxing",
|
||||
|
@ -90,7 +90,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Bänder-Meister",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Teamwork",
|
||||
description: "Nutze Staffette, während der Anwender mindestens eines Statuswertes maximiert hat.",
|
||||
},
|
||||
@ -333,7 +333,7 @@ export const PGFachv: AchievementTranslationEntries = {
|
||||
name: "Bänder-Meisterin",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": PGMachv.TRANSFER_MAX_BATTLE_STAT,
|
||||
"TRANSFER_MAX_STAT_STAGE": PGMachv.TRANSFER_MAX_STAT_STAGE,
|
||||
"MAX_FRIENDSHIP": PGMachv.MAX_FRIENDSHIP,
|
||||
"MEGA_EVOLVE": PGMachv.MEGA_EVOLVE,
|
||||
"GIGANTAMAX": PGMachv.GIGANTAMAX,
|
||||
|
@ -3,7 +3,6 @@ import { PokemonInfoTranslationEntries } from "#app/interfaces/locales";
|
||||
export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
Stat: {
|
||||
"HP": "KP",
|
||||
"HPStat": "KP",
|
||||
"HPshortened": "KP",
|
||||
"ATK": "Angriff",
|
||||
"ATKshortened": "Ang",
|
||||
|
@ -89,9 +89,9 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Master League Champion",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Teamwork",
|
||||
description: "Baton pass to another party member with at least one stat maxed out",
|
||||
description: "Baton pass to another party member with at least one stat stage maxed out",
|
||||
},
|
||||
"MAX_FRIENDSHIP": {
|
||||
name: "Friendmaxxing",
|
||||
|
@ -3,7 +3,7 @@ import { PokemonInfoTranslationEntries } from "#app/interfaces/locales";
|
||||
export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
Stat: {
|
||||
"HP": "Max. HP",
|
||||
"HPshortened": "MaxHP",
|
||||
"HPshortened": "HP",
|
||||
"ATK": "Attack",
|
||||
"ATKshortened": "Atk",
|
||||
"DEF": "Defense",
|
||||
@ -16,7 +16,6 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
"SPDshortened": "Spd",
|
||||
"ACC": "Accuracy",
|
||||
"EVA": "Evasiveness",
|
||||
"HPStat": "HP"
|
||||
},
|
||||
|
||||
Type: {
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Campeón Liga Master",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Trabajo en Equipo",
|
||||
description: "Haz relevo a otro miembro del equipo con al menos una estadística al máximo.",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Master Maitre de la Ligue",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Travail d’équipe",
|
||||
description: "Utiliser Relais avec au moins une statistique montée à fond",
|
||||
},
|
||||
@ -363,7 +363,7 @@ export const PGFachv: AchievementTranslationEntries = {
|
||||
name: "Master Maitresse de la Ligue",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Travail d’équipe",
|
||||
description: "Utiliser Relais avec au moins une statistique montée à fond",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Campione Lega Assoluta",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Lavoro di Squadra",
|
||||
description: "Trasferisci almeno sei bonus statistiche tramite staffetta",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "マスターリーグチャンピオン",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "同力",
|
||||
description: "少なくとも 一つの 能力を 最大まで あげて 他の 手持ちポケモンに バトンタッチする",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "마스터 리그 챔피언",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "팀워크",
|
||||
description: "한 개 이상의 능력치가 최대 랭크일 때 배턴터치 사용",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "Fita de Diamante",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Trabalho em Equipe",
|
||||
description: "Use Baton Pass com pelo menos um atributo aumentado ao máximo",
|
||||
},
|
||||
@ -364,7 +364,7 @@ export const PGFachv: AchievementTranslationEntries = {
|
||||
name: "Fita de Diamante",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "Trabalho em Equipe",
|
||||
description: "Use Baton Pass com pelo menos um atributo aumentado ao máximo",
|
||||
},
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "大师球联盟冠军",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "团队协作",
|
||||
description: "在一项属性强化至最大时用接力棒传递给其他宝可梦",
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ import { PokemonInfoTranslationEntries } from "#app/interfaces/locales";
|
||||
export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
Stat: {
|
||||
"HP": "最大HP",
|
||||
"HPshortened": "最大HP",
|
||||
"HPshortened": "HP",
|
||||
"ATK": "攻击",
|
||||
"ATKshortened": "攻击",
|
||||
"DEF": "防御",
|
||||
|
@ -89,7 +89,7 @@ export const PGMachv: AchievementTranslationEntries = {
|
||||
name: "大師球聯盟冠軍",
|
||||
},
|
||||
|
||||
"TRANSFER_MAX_BATTLE_STAT": {
|
||||
"TRANSFER_MAX_STAT_STAGE": {
|
||||
name: "團隊協作",
|
||||
description: "在一項屬性強化至最大時用接力棒傳遞給其他寶可夢",
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ import { BerryType } from "#enums/berry-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||
import { PermanentStat, TEMP_BATTLE_STATS, TempBattleStat, Stat } from "#app/enums/stat";
|
||||
import { PermanentStat, TEMP_BATTLE_STATS, TempBattleStat, Stat, getStatKey } from "#app/enums/stat";
|
||||
|
||||
const outputModifierData = false;
|
||||
const useMaxWeightForOutput = false;
|
||||
@ -443,7 +443,7 @@ export class TempStatStageBoosterModifierType extends ModifierType implements Ge
|
||||
}
|
||||
|
||||
getDescription(_scene: BattleScene): string {
|
||||
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { stat: i18next.t(`pokemonInfo:Stat.${Stat[this.stat]}`) });
|
||||
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) });
|
||||
}
|
||||
|
||||
getPregenArgs(): any[] {
|
||||
@ -609,7 +609,7 @@ export class BaseStatBoosterModifierType extends PokemonHeldItemModifierType imp
|
||||
}
|
||||
|
||||
getDescription(_scene: BattleScene): string {
|
||||
return i18next.t("modifierType:ModifierType.BaseStatBoosterModifierType.description", { stat: i18next.t(`pokemonInfo:Stat.${Stat[this.stat]}`) });
|
||||
return i18next.t("modifierType:ModifierType.BaseStatBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) });
|
||||
}
|
||||
|
||||
getPregenArgs(): any[] {
|
||||
|
@ -1,10 +1,9 @@
|
||||
import BattleScene, { bypassLogin } from "./battle-scene";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
|
||||
import * as Utils from "./utils";
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr, MoveHeaderAttr } from "./data/move";
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatStageChangeAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr, MoveHeaderAttr } from "./data/move";
|
||||
import { Mode } from "./ui/ui";
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import { Stat } from "./data/pokemon-stat";
|
||||
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier, ResetNegativeStatStageModifier } from "./modifier/modifier";
|
||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
||||
@ -14,7 +13,6 @@ import { SummaryUiMode } from "./ui/summary-ui-handler";
|
||||
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||
import { EvolutionPhase } from "./evolution-phase";
|
||||
import { Phase } from "./phase";
|
||||
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
|
||||
import { biomeLinks, getBiomeName } from "./data/biomes";
|
||||
import { ModifierTier } from "./modifier/modifier-tier";
|
||||
import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
|
||||
@ -25,7 +23,7 @@ import { Starter } from "./ui/starter-select-ui-handler";
|
||||
import { Gender } from "./data/gender";
|
||||
import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
|
||||
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
||||
import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability";
|
||||
import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatStageChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatStageChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatStageChangeAbAttr, applyPostStatStageChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatStageChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability";
|
||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||
import { getBiomeKey } from "./field/arena";
|
||||
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
|
||||
@ -66,6 +64,7 @@ import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { applyChallenges, ChallengeType } from "./data/challenge";
|
||||
import { pokemonEvolutions } from "./data/pokemon-evolutions";
|
||||
import { Stat, BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
||||
|
||||
const { t } = i18next;
|
||||
|
||||
@ -718,8 +717,8 @@ export abstract class FieldPhase extends BattlePhase {
|
||||
}, this.scene.currentBattle.turn, this.scene.waveSeed);
|
||||
|
||||
orderedTargets.sort((a: Pokemon, b: Pokemon) => {
|
||||
const aSpeed = a?.getBattleStat(Stat.SPD) || 0;
|
||||
const bSpeed = b?.getBattleStat(Stat.SPD) || 0;
|
||||
const aSpeed = a?.getEffectiveStat(Stat.SPD) || 0;
|
||||
const bSpeed = b?.getEffectiveStat(Stat.SPD) || 0;
|
||||
|
||||
return bSpeed - aSpeed;
|
||||
});
|
||||
@ -3449,22 +3448,22 @@ export class ShowAbilityPhase extends PokemonPhase {
|
||||
|
||||
export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void;
|
||||
|
||||
export class StatChangePhase extends PokemonPhase {
|
||||
export class StatStageChangePhase extends PokemonPhase { // TODO: BattleStat
|
||||
private stats: BattleStat[];
|
||||
private selfTarget: boolean;
|
||||
private levels: integer;
|
||||
private stages: integer;
|
||||
private showMessage: boolean;
|
||||
private ignoreAbilities: boolean;
|
||||
private canBeCopied: boolean;
|
||||
private onChange: StatChangeCallback | null;
|
||||
|
||||
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) {
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], stages: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) {
|
||||
super(scene, battlerIndex);
|
||||
|
||||
this.selfTarget = selfTarget;
|
||||
this.stats = stats;
|
||||
this.levels = levels;
|
||||
this.stages = stages;
|
||||
this.showMessage = showMessage;
|
||||
this.ignoreAbilities = ignoreAbilities;
|
||||
this.canBeCopied = canBeCopied;
|
||||
@ -3490,21 +3489,21 @@ export class StatChangePhase extends PokemonPhase {
|
||||
const filteredStats = this.stats.map(s => s !== BattleStat.RAND ? s : this.getRandomStat()).filter(stat => {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
|
||||
if (!this.selfTarget && this.levels < 0) {
|
||||
if (!this.selfTarget && this.stages < 0) {
|
||||
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled);
|
||||
}
|
||||
|
||||
if (!cancelled.value && !this.selfTarget && this.levels < 0) {
|
||||
applyPreStatChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled);
|
||||
if (!cancelled.value && !this.selfTarget && this.stages < 0) {
|
||||
applyPreStatStageChangeAbAttrs(ProtectStatAbAttr, this.getPokemon(), stat, cancelled);
|
||||
}
|
||||
|
||||
return !cancelled.value;
|
||||
});
|
||||
|
||||
const levels = new Utils.IntegerHolder(this.levels);
|
||||
const levels = new Utils.IntegerHolder(this.stages);
|
||||
|
||||
if (!this.ignoreAbilities) {
|
||||
applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, levels);
|
||||
applyAbAttrs(StatStageChangeMultiplierAbAttr, pokemon, null, levels);
|
||||
}
|
||||
|
||||
const battleStats = this.getPokemon().summonData.battleStats;
|
||||
@ -3526,20 +3525,20 @@ export class StatChangePhase extends PokemonPhase {
|
||||
|
||||
if (levels.value > 0 && this.canBeCopied) {
|
||||
for (const opponent of pokemon.getOpponents()) {
|
||||
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value);
|
||||
applyAbAttrs(StatStageChangeCopyAbAttr, opponent, null, this.stats, levels.value);
|
||||
}
|
||||
}
|
||||
|
||||
applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget);
|
||||
applyPostStatStageChangeAbAttrs(PostStatStageChangeAbAttr, pokemon, filteredStats, this.levels, 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 StatChangePhase && p.battlerIndex === this.battlerIndex);
|
||||
if (!(existingPhase instanceof StatChangePhase)) {
|
||||
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 PokemonResetNegativeStatStageModifier;
|
||||
const whiteHerb = this.scene.applyModifier(ResetNegativeStatStageModifier, this.player, pokemon) as ResetNegativeStatStageModifier;
|
||||
// If the White Herb was applied, consume it
|
||||
if (whiteHerb) {
|
||||
--whiteHerb.stackCount;
|
||||
whiteHerb.stackCount--;
|
||||
if (whiteHerb.stackCount <= 0) {
|
||||
this.scene.removeModifier(whiteHerb);
|
||||
}
|
||||
@ -3563,7 +3562,7 @@ export class StatChangePhase extends PokemonPhase {
|
||||
|
||||
// On increase, show the red sprite located at ATK
|
||||
// On decrease, show the blue sprite located at SPD
|
||||
const spriteColor = levels.value >= 1 ? BattleStat[BattleStat.ATK].toLowerCase() : BattleStat[BattleStat.SPD].toLowerCase();
|
||||
const spriteColor = levels.value >= 1 ? Stat[Stat.ATK].toLowerCase() : Stat[Stat.SPD].toLowerCase();
|
||||
const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, "battle_stats", spriteColor);
|
||||
statSprite.setPipeline(this.scene.fieldSpritePipeline);
|
||||
statSprite.setAlpha(0);
|
||||
@ -3657,11 +3656,19 @@ export class StatChangePhase extends PokemonPhase {
|
||||
if (relLevelStats.length > 1) {
|
||||
statsFragment = relLevelStats.length >= 5
|
||||
? i18next.t("battle:stats")
|
||||
: `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(", ")}${relLevelStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`;
|
||||
messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length));
|
||||
: `${relLevelStats.slice(0, -1).map(s => i18next.t(getStatKey(s))).join(", ")}${relLevelStats.length > 2 ? "," : ""} ${i18next.t("battle:statsAnd")} ${i18next.t(getStatKey(relLevelStats[relLevelStats.length - 1]))}`;
|
||||
messages.push(i18next.t(getStatStageChangeDescriptionKey(Math.abs(parseInt(rl)), levels >= 1), {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(this.getPokemon()),
|
||||
stats: statsFragment,
|
||||
count: relLevelStats.length
|
||||
}));
|
||||
} else {
|
||||
statsFragment = getBattleStatName(relLevelStats[0]);
|
||||
messages.push(getBattleStatLevelChangeDescription(getPokemonNameWithAffix(this.getPokemon()), statsFragment, Math.abs(parseInt(rl)), levels >= 1,relLevelStats.length));
|
||||
statsFragment = i18next.t(getStatKey(relLevelStats[0]));
|
||||
messages.push(i18next.t(getStatStageChangeDescriptionKey(Math.abs(parseInt(rl)), levels >= 1), {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(this.getPokemon()),
|
||||
stats: statsFragment,
|
||||
count: relLevelStats.length
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@ -3985,7 +3992,7 @@ export class FaintPhase extends PokemonPhase {
|
||||
if (defeatSource?.isOnField()) {
|
||||
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
||||
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
||||
const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr);
|
||||
const pvattrs = pvmove.getAttrs(PostVictoryStatStageChangeAttr);
|
||||
if (pvattrs.length) {
|
||||
for (const pvattr of pvattrs) {
|
||||
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
|
||||
|
@ -8,6 +8,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||
import { ParseKeys } from "i18next";
|
||||
import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge.js";
|
||||
import { ConditionFn } from "#app/@types/common.js";
|
||||
import { getShortenedStatKey, Stat } from "#app/enums/stat.js";
|
||||
|
||||
export enum AchvTier {
|
||||
COMMON,
|
||||
@ -178,13 +179,13 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||
case "10000_DMG":
|
||||
return i18next.t(`${genderPrefix}achv:DamageAchv.description` as ParseKeys, {"damageAmount": achvs._10000_DMG.damageAmount.toLocaleString("en-US")});
|
||||
case "250_HEAL":
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._250_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")});
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._250_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP)) });
|
||||
case "1000_HEAL":
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._1000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")});
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._1000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP)) });
|
||||
case "2500_HEAL":
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._2500_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")});
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._2500_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP)) });
|
||||
case "10000_HEAL":
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._10000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t("pokemonInfo:Stat.HPshortened")});
|
||||
return i18next.t(`${genderPrefix}achv:HealAchv.description` as ParseKeys, {"healAmount": achvs._10000_HEAL.healAmount.toLocaleString("en-US"), "HP": i18next.t(getShortenedStatKey(Stat.HP)) });
|
||||
case "LV_100":
|
||||
return i18next.t(`${genderPrefix}achv:LevelAchv.description` as ParseKeys, {"level": achvs.LV_100.level});
|
||||
case "LV_250":
|
||||
@ -201,8 +202,8 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||
return i18next.t(`${genderPrefix}achv:RibbonAchv.description` as ParseKeys, {"ribbonAmount": achvs._75_RIBBONS.ribbonAmount.toLocaleString("en-US")});
|
||||
case "100_RIBBONS":
|
||||
return i18next.t(`${genderPrefix}achv:RibbonAchv.description` as ParseKeys, {"ribbonAmount": achvs._100_RIBBONS.ribbonAmount.toLocaleString("en-US")});
|
||||
case "TRANSFER_MAX_BATTLE_STAT":
|
||||
return i18next.t(`${genderPrefix}achv:TRANSFER_MAX_BATTLE_STAT.description` as ParseKeys);
|
||||
case "TRANSFER_MAX_STAT_STAGE":
|
||||
return i18next.t(`${genderPrefix}achv:TRANSFER_MAX_STAT_STAGE.description` as ParseKeys);
|
||||
case "MAX_FRIENDSHIP":
|
||||
return i18next.t(`${genderPrefix}achv:MAX_FRIENDSHIP.description` as ParseKeys);
|
||||
case "MEGA_EVOLVE":
|
||||
@ -309,7 +310,7 @@ export const achvs = {
|
||||
_50_RIBBONS: new RibbonAchv("50_RIBBONS","", 50, "ultra_ribbon", 50).setSecret(true),
|
||||
_75_RIBBONS: new RibbonAchv("75_RIBBONS","", 75, "rogue_ribbon", 75).setSecret(true),
|
||||
_100_RIBBONS: new RibbonAchv("100_RIBBONS","", 100, "master_ribbon", 100).setSecret(true),
|
||||
TRANSFER_MAX_BATTLE_STAT: new Achv("TRANSFER_MAX_BATTLE_STAT","", "TRANSFER_MAX_BATTLE_STAT.description","baton", 20),
|
||||
TRANSFER_MAX_STAT_STAGE: new Achv("TRANSFER_MAX_STAT_STAGE","", "TRANSFER_MAX_STAT_STAGE.description","baton", 20),
|
||||
MAX_FRIENDSHIP: new Achv("MAX_FRIENDSHIP", "", "MAX_FRIENDSHIP.description","soothe_bell", 25),
|
||||
MEGA_EVOLVE: new Achv("MEGA_EVOLVE", "", "MEGA_EVOLVE.description","mega_bracelet", 50),
|
||||
GIGANTAMAX: new Achv("GIGANTAMAX", "", "GIGANTAMAX.description","dynamax_band", 50),
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Abilities } from "#app/enums/abilities.js";
|
||||
import { Moves } from "#app/enums/moves.js";
|
||||
import { Species } from "#app/enums/species.js";
|
||||
@ -35,7 +35,7 @@ describe("Abilities - COSTAR", () => {
|
||||
|
||||
|
||||
test(
|
||||
"ability copies positive stat changes",
|
||||
"ability copies positive stat stages",
|
||||
async () => {
|
||||
game.override.enemyAbility(Abilities.BALL_FETCH);
|
||||
|
||||
@ -48,8 +48,8 @@ describe("Abilities - COSTAR", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2);
|
||||
expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0);
|
||||
expect(leftPokemon.getStatStage(Stat.SPATK)).toBe(2);
|
||||
expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
@ -57,14 +57,14 @@ describe("Abilities - COSTAR", () => {
|
||||
await game.phaseInterceptor.to(MessagePhase);
|
||||
|
||||
[leftPokemon, rightPokemon] = game.scene.getPlayerField();
|
||||
expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2);
|
||||
expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2);
|
||||
expect(leftPokemon.getStatStage(Stat.SPATK)).toBe(2);
|
||||
expect(rightPokemon.getStatStage(Stat.SPATK)).toBe(2);
|
||||
},
|
||||
TIMEOUT,
|
||||
);
|
||||
|
||||
test(
|
||||
"ability copies negative stat changes",
|
||||
"ability copies negative stat stages",
|
||||
async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE);
|
||||
|
||||
@ -72,8 +72,8 @@ describe("Abilities - COSTAR", () => {
|
||||
|
||||
let [leftPokemon, rightPokemon] = game.scene.getPlayerField();
|
||||
|
||||
expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);
|
||||
expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);
|
||||
expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
@ -81,8 +81,8 @@ describe("Abilities - COSTAR", () => {
|
||||
await game.phaseInterceptor.to(MessagePhase);
|
||||
|
||||
[leftPokemon, rightPokemon] = game.scene.getPlayerField();
|
||||
expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);
|
||||
expect(rightPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);
|
||||
expect(leftPokemon.getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(rightPokemon.getStatStage(Stat.ATK)).toBe(-2);
|
||||
},
|
||||
TIMEOUT,
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StatusEffect } from "#app/enums/status-effect.js";
|
||||
import Pokemon from "#app/field/pokemon.js";
|
||||
|
||||
@ -95,7 +95,7 @@ describe("Abilities - Gulp Missile", () => {
|
||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||
});
|
||||
|
||||
it("deals ¼ of the attacker's maximum HP when hit by a damaging attack", async () => {
|
||||
it("deals 1/4 of the attacker's maximum HP when hit by a damaging attack", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
|
||||
await game.startBattle([Species.CRAMORANT]);
|
||||
|
||||
@ -127,7 +127,7 @@ describe("Abilities - Gulp Missile", () => {
|
||||
expect(cramorant.formIndex).toBe(GULPING_FORM);
|
||||
});
|
||||
|
||||
it("lowers the attacker's Defense by 1 stage when hit in Gulping form", async () => {
|
||||
it("lowers attacker's DEF stat stage by 1 when hit in Gulping form", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
|
||||
await game.startBattle([Species.CRAMORANT]);
|
||||
|
||||
@ -146,7 +146,7 @@ describe("Abilities - Gulp Missile", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy));
|
||||
expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined();
|
||||
expect(cramorant.formIndex).toBe(NORMAL_FORM);
|
||||
});
|
||||
@ -207,7 +207,7 @@ describe("Abilities - Gulp Missile", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemy.hp).toBe(enemyHpPreEffect);
|
||||
expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined();
|
||||
expect(cramorant.formIndex).toBe(NORMAL_FORM);
|
||||
});
|
||||
|
@ -41,13 +41,13 @@ describe("Abilities - Hustle", () => {
|
||||
const pikachu = game.scene.getPlayerPokemon()!;
|
||||
const atk = pikachu.stats[Stat.ATK];
|
||||
|
||||
vi.spyOn(pikachu, "getBattleStat");
|
||||
vi.spyOn(pikachu, "getEffectiveStat");
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
|
||||
await game.move.forceHit();
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
|
||||
expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5);
|
||||
expect(pikachu.getEffectiveStat).toHaveReturnedWith(atk * 1.5);
|
||||
});
|
||||
|
||||
it("lowers the accuracy of the user's physical moves by 20%", async () => {
|
||||
@ -67,13 +67,13 @@ describe("Abilities - Hustle", () => {
|
||||
const pikachu = game.scene.getPlayerPokemon()!;
|
||||
const spatk = pikachu.stats[Stat.SPATK];
|
||||
|
||||
vi.spyOn(pikachu, "getBattleStat");
|
||||
vi.spyOn(pikachu, "getEffectiveStat");
|
||||
vi.spyOn(pikachu, "getAccuracyMultiplier");
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.GIGA_DRAIN));
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
|
||||
expect(pikachu.getBattleStat).toHaveReturnedWith(spatk);
|
||||
expect(pikachu.getEffectiveStat).toHaveReturnedWith(spatk);
|
||||
expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1);
|
||||
});
|
||||
|
||||
|
@ -2,12 +2,9 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { generateStarter, getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||
import { GameModes, getGameMode } from "#app/game-mode";
|
||||
import { CommandPhase, DamagePhase, EncounterPhase, EnemyCommandPhase, SelectStarterPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { CommandPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -38,7 +35,7 @@ describe("Abilities - Intimidate", () => {
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("single - wild with switch", async () => {
|
||||
it("should lower ATK stat stage by 1 of enemy Pokemon on entry and player switch", async () => {
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
game.onNextPrompt(
|
||||
"CheckSwitchPhase",
|
||||
@ -50,108 +47,25 @@ describe("Abilities - Intimidate", () => {
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.MIGHTYENA);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
let playerPokemon = game.scene.getPlayerPokemon();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
|
||||
expect(playerPokemon!.species.speciesId).toBe(Species.MIGHTYENA);
|
||||
expect(enemyPokemon!.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon!.getStatStage(Stat.ATK)).toBe(-1);
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.phaseInterceptor.run(CommandPhase);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA);
|
||||
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(0);
|
||||
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
playerPokemon = game.scene.getPlayerPokemon();
|
||||
expect(playerPokemon!.species.speciesId).toBe(Species.POOCHYENA);
|
||||
expect(playerPokemon!.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon!.getStatStage(Stat.ATK)).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("single - boss should only trigger once then switch", async () => {
|
||||
game.override.startingWave(10);
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
game.onNextPrompt(
|
||||
"CheckSwitchPhase",
|
||||
Mode.CONFIRM,
|
||||
() => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
game.doSwitchPokemon(1);
|
||||
await game.phaseInterceptor.run(CommandPhase);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA);
|
||||
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(0);
|
||||
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("single - trainer should only trigger once with switch", async () => {
|
||||
game.override.startingWave(5);
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
game.onNextPrompt(
|
||||
"CheckSwitchPhase",
|
||||
Mode.CONFIRM,
|
||||
() => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
game.doSwitchPokemon(1);
|
||||
await game.phaseInterceptor.run(CommandPhase);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.POOCHYENA);
|
||||
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(0);
|
||||
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
}, 200000);
|
||||
|
||||
it("double - trainer should only trigger once per pokemon", async () => {
|
||||
game.override.battleType("double");
|
||||
game.override.startingWave(5);
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
game.onNextPrompt(
|
||||
"CheckSwitchPhase",
|
||||
Mode.CONFIRM,
|
||||
() => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats;
|
||||
expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2);
|
||||
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
|
||||
const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats;
|
||||
expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("double - wild: should only trigger once per pokemon", async () => {
|
||||
it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => {
|
||||
game.override.battleType("double");
|
||||
game.override.startingWave(3);
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
@ -165,210 +79,60 @@ describe("Abilities - Intimidate", () => {
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats;
|
||||
expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2);
|
||||
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
const playerField = game.scene.getPlayerField()!;
|
||||
const enemyField = game.scene.getEnemyPokemon()!;
|
||||
|
||||
const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats;
|
||||
expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2);
|
||||
expect(enemyField[0].getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(enemyField[1].getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(playerField[0].getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(playerField[1].getStatStage(Stat.ATK)).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("double - boss: should only trigger once per pokemon", async () => {
|
||||
game.override.battleType("double");
|
||||
game.override.startingWave(10);
|
||||
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
game.onNextPrompt(
|
||||
"CheckSwitchPhase",
|
||||
Mode.CONFIRM,
|
||||
() => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)
|
||||
);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-2);
|
||||
const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats;
|
||||
expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-2);
|
||||
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
|
||||
const battleStatsPokemon2 = game.scene.getParty()[1].summonData.battleStats;
|
||||
expect(battleStatsPokemon2[BattleStat.ATK]).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("single - wild next wave opp triger once, us: none", async () => {
|
||||
game.override.startingWave(2);
|
||||
game.override.moveset([Moves.AERIAL_ACE]);
|
||||
await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase);
|
||||
await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
|
||||
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
|
||||
await game.toNextWave();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(0);
|
||||
}, 20000);
|
||||
|
||||
it("single - wild next turn - no retrigger on next turn", async () => {
|
||||
it("should not activate again if there is no switch or new entry", async () => {
|
||||
game.override.startingWave(2);
|
||||
game.override.moveset([Moves.SPLASH]);
|
||||
await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
console.log("===to new turn===");
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.toNextTurn();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
}, 20000);
|
||||
|
||||
it("single - trainer should only trigger once and each time he switch", async () => {
|
||||
it("should lower ATK stat stage by 1 for every switch", async () => {
|
||||
game.override.moveset([Moves.SPLASH]);
|
||||
game.override.enemyMoveset([Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH]);
|
||||
game.override.startingWave(5);
|
||||
await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
console.log("===to new turn===");
|
||||
await game.toNextTurn();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(0);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
let enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
console.log("===to new turn===");
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.toNextTurn();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-3);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(0);
|
||||
|
||||
enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.toNextTurn();
|
||||
|
||||
enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-3);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
}, 200000);
|
||||
|
||||
it("single - trainer should only trigger once whatever turn we are", async () => {
|
||||
game.override.moveset([Moves.SPLASH]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.startingWave(5);
|
||||
await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
console.log("===to new turn===");
|
||||
await game.toNextTurn();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
console.log("===to new turn===");
|
||||
await game.toNextTurn();
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
}, 20000);
|
||||
|
||||
it("double - wild vs only 1 on player side", async () => {
|
||||
game.override.battleType("double");
|
||||
game.override.startingWave(3);
|
||||
await game.runToSummon([Species.MIGHTYENA]);
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats;
|
||||
expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
}, 20000);
|
||||
|
||||
it("double - wild vs only 1 alive on player side", async () => {
|
||||
game.override.battleType("double");
|
||||
game.override.startingWave(3);
|
||||
await game.runToTitle();
|
||||
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
game.scene.gameMode = getGameMode(GameModes.CLASSIC);
|
||||
const starters = generateStarter(game.scene, [Species.MIGHTYENA, Species.POOCHYENA]);
|
||||
const selectStarterPhase = new SelectStarterPhase(game.scene);
|
||||
game.scene.pushPhase(new EncounterPhase(game.scene, false));
|
||||
selectStarterPhase.initBattle(starters);
|
||||
game.scene.getParty()[1].hp = 0;
|
||||
game.scene.getParty()[1].status = new Status(StatusEffect.FAINT);
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.run(EncounterPhase);
|
||||
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
const battleStatsOpponent2 = game.scene.currentBattle.enemyParty[1].summonData.battleStats;
|
||||
expect(battleStatsOpponent2[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-2);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CommandPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -29,14 +29,17 @@ describe("Abilities - Intrepid Sword", () => {
|
||||
game.override.ability(Abilities.INTREPID_SWORD);
|
||||
});
|
||||
|
||||
it("INTREPID SWORD on player", async() => {
|
||||
it("should raise ATK stat stage by 1 on entry", async() => {
|
||||
await game.runToSummon([
|
||||
Species.ZACIAN,
|
||||
]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
await game.phaseInterceptor.to(CommandPhase, false);
|
||||
const battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(1);
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(1);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#app/data/pokemon-stat";
|
||||
import { CommandPhase, EnemyCommandPhase, VictoryPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase, TurnEndPhase, VictoryPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
|
||||
describe("Abilities - Moxie", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -34,29 +32,46 @@ describe("Abilities - Moxie", () => {
|
||||
game.override.enemyAbility(Abilities.MOXIE);
|
||||
game.override.ability(Abilities.MOXIE);
|
||||
game.override.startingLevel(2000);
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("MOXIE", async() => {
|
||||
it("should raise ATK stat stage by 1 when defeating an enemy Pokemon", async() => {
|
||||
const moveToUse = Moves.AERIAL_ACE;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[Stat.ATK]).toBe(0);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase);
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(1);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
}, 20000);
|
||||
|
||||
it("should raise ATK stat stage by 1 when defeating an ally Pokemon", async() => {
|
||||
game.override.battleType("double");
|
||||
const moveToUse = Moves.AERIAL_ACE;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
|
||||
const [ firstPokemon, secondPokemon ] = game.scene.getPlayerField();
|
||||
|
||||
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
secondPokemon.hp = 1;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
game.doSelectTarget(BattlerIndex.PLAYER_2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StatusEffect } from "#app/data/status-effect.js";
|
||||
import { Type } from "#app/data/type.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
@ -91,7 +91,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(2);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -111,7 +111,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES));
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -563,7 +563,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -585,7 +585,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability.js";
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { StatStageMultiplierAbAttr, allAbilities } from "#app/data/ability.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { WeatherType } from "#app/data/weather.js";
|
||||
import { CommandPhase, MoveEffectPhase, MoveEndPhase } from "#app/phases.js";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -48,10 +48,10 @@ describe("Abilities - Sand Veil", () => {
|
||||
|
||||
vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[Abilities.SAND_VEIL]);
|
||||
|
||||
const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0];
|
||||
vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation(
|
||||
(pokemon, passive, battleStat, statValue, args) => {
|
||||
if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
||||
const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(StatStageMultiplierAbAttr)[0];
|
||||
vi.spyOn(sandVeilAttr, "applyStatStage").mockImplementation(
|
||||
(_pokemon, _passive, stat, statValue, _args) => {
|
||||
if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
||||
statValue.value *= -1; // will make all attacks miss
|
||||
return true;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TerrainType } from "#app/data/terrain.js";
|
||||
import { MoveEndPhase, TurnEndPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
@ -9,6 +9,7 @@ import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
|
||||
// See also: TypeImmunityAbAttr
|
||||
describe("Abilities - Sap Sipper", () => {
|
||||
@ -31,52 +32,55 @@ describe("Abilities - Sap Sipper", () => {
|
||||
game.override.disableCrits();
|
||||
});
|
||||
|
||||
it("raise attack 1 level and block effects when activated against a grass attack", async() => {
|
||||
it("raises ATK stat stage by 1 and block effects when activated against a grass attack", async() => {
|
||||
const moveToUse = Moves.LEAFAGE;
|
||||
const enemyAbility = Abilities.SAP_SIPPER;
|
||||
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.enemySpecies(Species.DUSKULL);
|
||||
game.override.enemyAbility(enemyAbility);
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const startingOppHp = game.scene.currentBattle.enemyParty[0].hp;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
const initialEnemyHp = enemyPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0);
|
||||
expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(initialEnemyHp - enemyPokemon.hp).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
|
||||
it("raise attack 1 level and block effects when activated against a grass status move", async() => {
|
||||
it("raises ATK stat stage by 1 and block effects when activated against a grass status move", async() => {
|
||||
const moveToUse = Moves.SPORE;
|
||||
const enemyAbility = Abilities.SAP_SIPPER;
|
||||
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyAbility(enemyAbility);
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(game.scene.getEnemyParty()[0].status).toBeUndefined();
|
||||
expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(enemyPokemon.status).toBeUndefined();
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
|
||||
it("do not activate against status moves that target the field", async() => {
|
||||
const moveToUse = Moves.GRASSY_TERRAIN;
|
||||
const enemyAbility = Abilities.SAP_SIPPER;
|
||||
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyAbility(enemyAbility);
|
||||
|
||||
@ -88,51 +92,54 @@ describe("Abilities - Sap Sipper", () => {
|
||||
|
||||
expect(game.scene.arena.terrain).toBeDefined();
|
||||
expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY);
|
||||
expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("activate once against multi-hit grass attacks", async() => {
|
||||
const moveToUse = Moves.BULLET_SEED;
|
||||
const enemyAbility = Abilities.SAP_SIPPER;
|
||||
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyAbility(enemyAbility);
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const startingOppHp = game.scene.currentBattle.enemyParty[0].hp;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
const initialEnemyHp = enemyPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0);
|
||||
expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(initialEnemyHp - enemyPokemon.hp).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
|
||||
it("do not activate against status moves that target the user", async() => {
|
||||
const moveToUse = Moves.SPIKY_SHIELD;
|
||||
const ability = Abilities.SAP_SIPPER;
|
||||
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.ability(ability);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.NONE, Moves.NONE, Moves.NONE]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyAbility(Abilities.NONE);
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].getTag(BattlerTagType.SPIKY_SHIELD)).toBeDefined();
|
||||
expect(playerPokemon.getTag(BattlerTagType.SPIKY_SHIELD)).toBeDefined();
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||
});
|
||||
|
||||
@ -149,13 +156,14 @@ describe("Abilities - Sap Sipper", () => {
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const startingOppHp = game.scene.currentBattle.enemyParty[0].hp;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
const initialEnemyHp = enemyPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(startingOppHp - game.scene.getEnemyParty()[0].hp).toBe(0);
|
||||
expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(initialEnemyHp - enemyPokemon.hp).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnEndPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
@ -42,12 +42,14 @@ describe("Abilities - Volt Absorb", () => {
|
||||
|
||||
await game.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].summonData.battleStats[BattleStat.SPDEF]).toBe(1);
|
||||
expect(game.scene.getParty()[0].getTag(BattlerTagType.CHARGED)).toBeDefined();
|
||||
expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(1);
|
||||
expect(playerPokemon.getTag(BattlerTagType.CHARGED)).toBeDefined();
|
||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnEndPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
@ -32,37 +32,37 @@ describe("Abilities - Wind Rider", () => {
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("takes no damage from wind moves and its Attack is increased by one stage when hit by one", async () => {
|
||||
it("takes no damage from wind moves and its ATK stat stage is raised by 1 when hit by one", async () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
const shiftry = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.PETAL_BLIZZARD));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(shiftry.isFullHp()).toBe(true);
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
|
||||
it("Attack is increased by one stage when Tailwind is present on its side", async () => {
|
||||
it("ATK stat stage is raised by 1 when Tailwind is present on its side", async () => {
|
||||
game.override.ability(Abilities.WIND_RIDER);
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
|
||||
await game.startBattle([Species.SHIFTRY]);
|
||||
const shiftry = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
|
||||
it("does not increase Attack when Tailwind is present on opposing side", async () => {
|
||||
it("does not raise ATK stat stage when Tailwind is present on opposing side", async () => {
|
||||
game.override.ability(Abilities.WIND_RIDER);
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
|
||||
@ -70,33 +70,33 @@ describe("Abilities - Wind Rider", () => {
|
||||
const magikarp = game.scene.getEnemyPokemon()!;
|
||||
const shiftry = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("does not increase Attack when Tailwind is present on opposing side", async () => {
|
||||
it("does not raise ATK stat stage when Tailwind is present on opposing side", async () => {
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
|
||||
await game.startBattle([Species.SHIFTRY]);
|
||||
const magikarp = game.scene.getEnemyPokemon()!;
|
||||
const shiftry = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("does not interact with Sandstorm", async () => {
|
||||
@ -105,14 +105,14 @@ describe("Abilities - Wind Rider", () => {
|
||||
await game.startBattle([Species.SHIFTRY]);
|
||||
const shiftry = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(shiftry.isFullHp()).toBe(true);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SANDSTORM));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(shiftry.hp).lessThan(shiftry.getMaxHp());
|
||||
});
|
||||
});
|
||||
|
@ -224,7 +224,7 @@ describe("achvs", () => {
|
||||
expect(achvs._50_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._75_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._100_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs.TRANSFER_MAX_BATTLE_STAT).toBeInstanceOf(Achv);
|
||||
expect(achvs.TRANSFER_MAX_STAT_STAGE).toBeInstanceOf(Achv);
|
||||
expect(achvs.MAX_FRIENDSHIP).toBeInstanceOf(Achv);
|
||||
expect(achvs.MEGA_EVOLVE).toBeInstanceOf(Achv);
|
||||
expect(achvs.GIGANTAMAX).toBeInstanceOf(Achv);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat.js";
|
||||
import { BattleStat, getStatKey } from "#enums/stat";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { arrayOfRange, mockI18next } from "./utils/testUtils";
|
||||
|
||||
const TEST_BATTLE_STAT = -99 as unknown as BattleStat;
|
||||
const TEST_BATTLE_STAT = -99 as BattleStat;
|
||||
const TEST_POKEMON = "Testmon";
|
||||
const TEST_STAT = "Teststat";
|
||||
|
||||
@ -25,7 +25,7 @@ describe("battle-stat", () => {
|
||||
});
|
||||
|
||||
it("should fall back to ??? for an unknown BattleStat", () => {
|
||||
expect(getBattleStatName(TEST_BATTLE_STAT)).toBe("???");
|
||||
expect(getStatKey(TEST_BATTLE_STAT)).toBe("???");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { allSpecies } from "#app/data/pokemon-species";
|
||||
import { TempBattleStat } from "#app/data/temp-battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { GameModes } from "#app/game-mode";
|
||||
import { getGameMode } from "#app/game-mode.js";
|
||||
import {
|
||||
@ -339,7 +339,7 @@ describe("Test Battle Phase", () => {
|
||||
.startingLevel(100)
|
||||
.moveset([moveToUse])
|
||||
.enemyMoveset(SPLASH_ONLY)
|
||||
.startingHeldItems([{ name: "TEMP_STAT_BOOSTER", type: TempBattleStat.ACC }]);
|
||||
.startingHeldItems([{ name: "TEMP_STAT_BOOSTER", type: Stat.ACC }]);
|
||||
|
||||
await game.startBattle();
|
||||
game.scene.getPlayerPokemon()!.hp = 1;
|
||||
|
@ -3,14 +3,14 @@ import Pokemon from "#app/field/pokemon.js";
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags.js";
|
||||
import { StatChangePhase } from "#app/phases.js";
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
|
||||
vi.mock("#app/battle-scene.js");
|
||||
|
||||
describe("BattlerTag - OctolockTag", () => {
|
||||
describe("lapse behavior", () => {
|
||||
it("unshifts a StatChangePhase with expected stat changes", { timeout: 10000 }, async () => {
|
||||
it("unshifts a StatChangePhase with expected stat stage changes", { timeout: 10000 }, async () => {
|
||||
const mockPokemon = {
|
||||
scene: new BattleScene(),
|
||||
getBattlerIndex: () => 0,
|
||||
@ -20,8 +20,8 @@ describe("BattlerTag - OctolockTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(-1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual([BattleStat.DEF, BattleStat.SPDEF]);
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(-1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual([ Stat.DEF, Stat.SPDEF ]);
|
||||
});
|
||||
|
||||
subject.lapse(mockPokemon, BattlerTagLapseType.TURN_END);
|
||||
|
@ -3,7 +3,7 @@ import Pokemon, { PokemonSummonData } from "#app/field/pokemon.js";
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import { StockpilingTag } from "#app/data/battler-tags.js";
|
||||
import { StatChangePhase } from "#app/phases.js";
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import * as messages from "#app/messages.js";
|
||||
|
||||
beforeEach(() => {
|
||||
@ -12,7 +12,7 @@ beforeEach(() => {
|
||||
|
||||
describe("BattlerTag - StockpilingTag", () => {
|
||||
describe("onAdd", () => {
|
||||
it("unshifts a StatChangePhase with expected stat changes on add", { timeout: 10000 }, async () => {
|
||||
it("unshifts a StatChangePhase with expected stat stage changes on add", { timeout: 10000 }, async () => {
|
||||
const mockPokemon = {
|
||||
scene: vi.mocked(new BattleScene()) as BattleScene,
|
||||
getBattlerIndex: () => 0,
|
||||
@ -24,10 +24,10 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]);
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [Stat.DEF, Stat.SPDEF], [1, 1]);
|
||||
});
|
||||
|
||||
subject.onAdd(mockPokemon);
|
||||
@ -44,17 +44,17 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "queueMessage").mockImplementation(() => {});
|
||||
|
||||
mockPokemon.summonData.battleStats[BattleStat.DEF] = 6;
|
||||
mockPokemon.summonData.battleStats[BattleStat.SPDEF] = 5;
|
||||
mockPokemon.setStatStage(Stat.DEF, 6);
|
||||
mockPokemon.setStatStage(Stat.SPDEF, 5);
|
||||
|
||||
const subject = new StockpilingTag(1);
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF]));
|
||||
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]);
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [1, 1]);
|
||||
});
|
||||
|
||||
subject.onAdd(mockPokemon);
|
||||
@ -76,10 +76,10 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]);
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [ Stat.DEF, Stat.SPDEF ], [1, 1]);
|
||||
});
|
||||
|
||||
subject.onOverlap(mockPokemon);
|
||||
@ -98,18 +98,18 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "queueMessage").mockImplementation(() => {});
|
||||
|
||||
mockPokemon.summonData.battleStats[BattleStat.DEF] = 5;
|
||||
mockPokemon.summonData.battleStats[BattleStat.SPDEF] = 4;
|
||||
mockPokemon.setStatStage(Stat.DEF, 5);
|
||||
mockPokemon.setStatStage(Stat.SPDEF, 4);
|
||||
|
||||
const subject = new StockpilingTag(1);
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||
|
||||
// def doesn't change
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]);
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [1]);
|
||||
});
|
||||
|
||||
subject.onAdd(mockPokemon);
|
||||
@ -117,11 +117,11 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||
|
||||
// def doesn't change
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]);
|
||||
(phase as StatChangePhase)["onChange"]!(mockPokemon, [ Stat.SPDEF ], [1]);
|
||||
});
|
||||
|
||||
subject.onOverlap(mockPokemon);
|
||||
@ -129,8 +129,8 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([ Stat.DEF, Stat.SPDEF ]));
|
||||
|
||||
// neither stat changes, stack count should still increase
|
||||
});
|
||||
@ -138,20 +138,20 @@ describe("BattlerTag - StockpilingTag", () => {
|
||||
subject.onOverlap(mockPokemon);
|
||||
expect(subject.stockpiledCount).toBe(3);
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => {
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(_phase => {
|
||||
throw new Error("Should not be called a fourth time");
|
||||
});
|
||||
|
||||
// fourth stack should not be applied
|
||||
subject.onOverlap(mockPokemon);
|
||||
expect(subject.stockpiledCount).toBe(3);
|
||||
expect(subject.statChangeCounts).toMatchObject({ [BattleStat.DEF]: 0, [BattleStat.SPDEF]: 2 });
|
||||
expect(subject.statChangeCounts).toMatchObject({ [ Stat.DEF ]: 0, [Stat.SPDEF]: 2 });
|
||||
|
||||
// removing tag should reverse stat changes
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementationOnce(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(-2);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.SPDEF]));
|
||||
expect((phase as StatChangePhase)["stages"]).toEqual(-2);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.SPDEF]));
|
||||
});
|
||||
|
||||
subject.onRemove(mockPokemon);
|
||||
|
@ -37,29 +37,29 @@ describe("Items - Eviolite", () => {
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
// Checking consoe log to make sure Eviolite is applied when getBattleStat (with the appropriate stat) is called
|
||||
partyMember.getBattleStat(Stat.DEF);
|
||||
// Checking consoe log to make sure Eviolite is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPDEF);
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.ATK);
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPATK);
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPD);
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
});
|
||||
|
||||
|
@ -37,29 +37,29 @@ describe("Items - Light Ball", () => {
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
// Checking consoe log to make sure Light Ball is applied when getBattleStat (with the appropriate stat) is called
|
||||
partyMember.getBattleStat(Stat.DEF);
|
||||
// Checking consoe log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), "");
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPDEF);
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.ATK);
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPATK);
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPD);
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.LIGHT_BALL.name"), "");
|
||||
});
|
||||
|
||||
|
@ -37,29 +37,29 @@ describe("Items - Metal Powder", () => {
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
// Checking consoe log to make sure Metal Powder is applied when getBattleStat (with the appropriate stat) is called
|
||||
partyMember.getBattleStat(Stat.DEF);
|
||||
// Checking consoe log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), "");
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPDEF);
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.ATK);
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPATK);
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPD);
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.METAL_POWDER.name"), "");
|
||||
});
|
||||
|
||||
|
@ -37,29 +37,29 @@ describe("Items - Quick Powder", () => {
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
// Checking consoe log to make sure Quick Powder is applied when getBattleStat (with the appropriate stat) is called
|
||||
partyMember.getBattleStat(Stat.DEF);
|
||||
// Checking consoe log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), "");
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPDEF);
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.ATK);
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPATK);
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPD);
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.QUICK_POWDER.name"), "");
|
||||
});
|
||||
|
||||
|
@ -37,29 +37,29 @@ describe("Items - Thick Club", () => {
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
// Checking consoe log to make sure Thick Club is applied when getBattleStat (with the appropriate stat) is called
|
||||
partyMember.getBattleStat(Stat.DEF);
|
||||
// Checking consoe log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), "");
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPDEF);
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.ATK);
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPATK);
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getBattleStat(Stat.SPD);
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:SpeciesBoosterItem.THICK_CLUB.name"), "");
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { PostSummonPhase, TurnEndPhase } from "#app/phases.js";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import { getMovePosition } from "#app/test/utils/gameManagerUtils";
|
||||
@ -35,7 +35,7 @@ describe("Moves - Baton Pass", () => {
|
||||
.disableCrits();
|
||||
});
|
||||
|
||||
it("passes stat stage buffs when player uses it", async() => {
|
||||
it("transfers all stat stages when player uses it", async() => {
|
||||
// arrange
|
||||
await game.startBattle([
|
||||
Species.RAICHU,
|
||||
@ -45,7 +45,10 @@ describe("Moves - Baton Pass", () => {
|
||||
// round 1 - buff
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT));
|
||||
await game.toNextTurn();
|
||||
expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2);
|
||||
|
||||
let playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2);
|
||||
|
||||
// round 2 - baton pass
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS));
|
||||
@ -53,16 +56,16 @@ describe("Moves - Baton Pass", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
// assert
|
||||
const playerPkm = game.scene.getPlayerPokemon()!;
|
||||
expect(playerPkm.species.speciesId).toEqual(Species.SHUCKLE);
|
||||
expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2);
|
||||
playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
expect(playerPokemon.species.speciesId).toEqual(Species.SHUCKLE);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2);
|
||||
}, 20000);
|
||||
|
||||
it("passes stat stage buffs when AI uses it", async() => {
|
||||
// arrange
|
||||
game.override
|
||||
.startingWave(5)
|
||||
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
|
||||
.enemyMoveset([ Moves.NASTY_PLOT, Moves.NASTY_PLOT, Moves.NASTY_PLOT, Moves.NASTY_PLOT ]);
|
||||
await game.startBattle([
|
||||
Species.RAICHU,
|
||||
Species.SHUCKLE
|
||||
@ -74,13 +77,13 @@ describe("Moves - Baton Pass", () => {
|
||||
|
||||
// round 2 - baton pass
|
||||
game.scene.getEnemyPokemon()!.hp = 100;
|
||||
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
|
||||
game.override.enemyMoveset([ Moves.BATON_PASS, Moves.BATON_PASS, Moves.BATON_PASS, Moves.BATON_PASS ]);
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(PostSummonPhase, false);
|
||||
|
||||
// assert
|
||||
// check buffs are still there
|
||||
expect(game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2);
|
||||
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPATK)).toEqual(2);
|
||||
// confirm that a switch actually happened. can't use species because I
|
||||
// can't find a way to override trainer parties with more than 1 pokemon species
|
||||
expect(game.scene.getEnemyPokemon()!.hp).not.toEqual(100);
|
||||
|
@ -5,7 +5,7 @@ import { TurnEndPhase } from "#app/phases";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
// RATIO : HP Cost of Move
|
||||
@ -39,7 +39,7 @@ describe("Moves - BELLY DRUM", () => {
|
||||
|
||||
// Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Belly_Drum_(move)
|
||||
|
||||
test("Belly Drum raises the user's Attack to its max, at the cost of 1/2 of its maximum HP",
|
||||
test("raises the user's ATK stat stage to its max, at the cost of 1/2 of its maximum HP",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
@ -50,47 +50,47 @@ describe("Moves - BELLY DRUM", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Belly Drum will still take effect if an uninvolved stat is at max",
|
||||
test("will still take effect if an uninvolved stat stage is at max",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
// Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = -3;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6;
|
||||
// Here - Stat.ATK -> -3 and Stat.SPATK -> 6
|
||||
leadPokemon.setStatStage(Stat.ATK, -3);
|
||||
leadPokemon.setStatStage(Stat.SPATK, 6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Belly Drum fails if the pokemon's attack stat is at its maximum",
|
||||
test("fails if the pokemon's ATK stat stage is at its maximum",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
leadPokemon.setStatStage(Stat.ATK, 6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Belly Drum fails if the user's health is less than 1/2",
|
||||
test("fails if the user's health is less than 1/2",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
@ -102,7 +102,7 @@ describe("Moves - BELLY DRUM", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { TurnEndPhase } from "#app/phases";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
@ -14,7 +14,7 @@ const RATIO = 3;
|
||||
/** Amount of extra HP lost */
|
||||
const PREDAMAGE = 15;
|
||||
|
||||
describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
describe("Moves - Clangorous Soul", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
@ -40,7 +40,7 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
|
||||
//Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move)
|
||||
|
||||
test("Clangorous Soul raises the user's Attack, Defense, Special Attack, Special Defense and Speed by one stage each, at the cost of 1/3 of its maximum HP",
|
||||
it("raises the user's ATK, DEF, SPATK, SPDEF, and SPD stat stages by 1 each at the cost of 1/3 of its maximum HP",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
@ -51,64 +51,64 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(1);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Clangorous Soul will still take effect if one or more of the involved stats are not at max",
|
||||
it("will still take effect if one or more of the involved stat stages are not at max",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
//Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.DEF] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 4;
|
||||
//Here - Stat.SPD -> 0 and Stat.SPDEF -> 4
|
||||
leadPokemon.setStatStage(Stat.ATK, 6);
|
||||
leadPokemon.setStatStage(Stat.DEF, 6);
|
||||
leadPokemon.setStatStage(Stat.SPATK, 6);
|
||||
leadPokemon.setStatStage(Stat.SPDEF, 4);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(5);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.DEF)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(5);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(1);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Clangorous Soul fails if all stats involved are at max",
|
||||
it("fails if all stat stages involved are at max",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.DEF] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPD] = 6;
|
||||
leadPokemon.setStatStage(Stat.ATK, 6);
|
||||
leadPokemon.setStatStage(Stat.DEF, 6);
|
||||
leadPokemon.setStatStage(Stat.SPATK, 6);
|
||||
leadPokemon.setStatStage(Stat.SPDEF, 6);
|
||||
leadPokemon.setStatStage(Stat.SPD, 6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.DEF)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test("Clangorous Soul fails if the user's health is less than 1/3",
|
||||
it("fails if the user's health is less than 1/3",
|
||||
async() => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
@ -120,11 +120,11 @@ describe("Moves - CLANGOROUS_SOUL", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Abilities } from "#app/enums/abilities.js";
|
||||
import { TurnEndPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
@ -33,20 +33,20 @@ describe("Moves - Double Team", () => {
|
||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||
});
|
||||
|
||||
it("increases the user's evasion by one stage.", async () => {
|
||||
it("raises the user's EVA stat stage by 1", async () => {
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
|
||||
const ally = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
vi.spyOn(enemy, "getAccuracyMultiplier");
|
||||
expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0);
|
||||
expect(ally.getStatStage(Stat.EVA)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_TEAM));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(1);
|
||||
expect(ally.getStatStage(Stat.EVA)).toBe(1);
|
||||
expect(enemy.getAccuracyMultiplier).toHaveReturnedWith(.75);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Type } from "#app/data/type";
|
||||
import { Species } from "#app/enums/species.js";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
@ -64,9 +64,8 @@ describe("Moves - Dragon Rage", () => {
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores resistances", async () => {
|
||||
@ -75,20 +74,18 @@ describe("Moves - Dragon Rage", () => {
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores stat changes", async () => {
|
||||
it("ignores SPATK stat stages", async () => {
|
||||
game.override.disableCrits();
|
||||
partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2;
|
||||
partyPokemon.setStatStage(Stat.SPATK, 2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores stab", async () => {
|
||||
@ -97,9 +94,8 @@ describe("Moves - Dragon Rage", () => {
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores criticals", async () => {
|
||||
@ -107,20 +103,18 @@ describe("Moves - Dragon Rage", () => {
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores damage modification from abilities such as ice scales", async () => {
|
||||
it("ignores damage modification from abilities, for example ICE_SCALES", async () => {
|
||||
game.override.disableCrits();
|
||||
game.override.enemyAbility(Abilities.ICE_SCALES);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
|
||||
it("ignores multi hit", async () => {
|
||||
@ -129,8 +123,7 @@ describe("Moves - Dragon Rage", () => {
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp;
|
||||
|
||||
expect(damageDealt).toBe(dragonRageDamage);
|
||||
expect(enemyPokemon.getInverseHp()).toBe(dragonRageDamage);
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import { TurnEndPhase } from "#app/phases";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
@ -51,9 +51,9 @@ describe("Moves - FILLET AWAY", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(2);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(2);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(2);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(2);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -64,17 +64,17 @@ describe("Moves - FILLET AWAY", () => {
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO);
|
||||
|
||||
//Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPATK] = 3;
|
||||
//Here - Stat.SPD -> 0 and Stat.SPATK -> 3
|
||||
leadPokemon.setStatStage(Stat.ATK, 6);
|
||||
leadPokemon.setStatStage(Stat.SPATK, 3);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(5);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(2);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(5);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(2);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -84,17 +84,17 @@ describe("Moves - FILLET AWAY", () => {
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
leadPokemon.summonData.battleStats[BattleStat.ATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6;
|
||||
leadPokemon.summonData.battleStats[BattleStat.SPD] = 6;
|
||||
leadPokemon.setStatStage(Stat.ATK, 6);
|
||||
leadPokemon.setStatStage(Stat.SPATK, 6);
|
||||
leadPokemon.setStatStage(Stat.SPD, 6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(6);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(6);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -110,9 +110,9 @@ describe("Moves - FILLET AWAY", () => {
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0);
|
||||
expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(leadPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Species } from "#app/enums/species.js";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import { DamagePhase, TurnEndPhase } from "#app/phases";
|
||||
@ -52,7 +52,7 @@ describe("Moves - Fissure", () => {
|
||||
game.scene.clearEnemyHeldItemModifiers();
|
||||
});
|
||||
|
||||
it("ignores damage modification from abilities such as fur coat", async () => {
|
||||
it("ignores damage modification from abilities, for example FUR_COAT", async () => {
|
||||
game.override.ability(Abilities.NO_GUARD);
|
||||
game.override.enemyAbility(Abilities.FUR_COAT);
|
||||
|
||||
@ -62,10 +62,10 @@ describe("Moves - Fissure", () => {
|
||||
expect(enemyPokemon.isFainted()).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores accuracy stat", async () => {
|
||||
it("ignores user's ACC stat stage", async () => {
|
||||
vi.spyOn(partyPokemon, "getAccuracyMultiplier");
|
||||
|
||||
enemyPokemon.summonData.battleStats[BattleStat.ACC] = -6;
|
||||
partyPokemon.setStatStage(Stat.ACC, -6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE));
|
||||
|
||||
@ -75,10 +75,10 @@ describe("Moves - Fissure", () => {
|
||||
expect(partyPokemon.getAccuracyMultiplier).toHaveReturnedWith(1);
|
||||
});
|
||||
|
||||
it("ignores evasion stat", async () => {
|
||||
it("ignores target's EVA stat stage", async () => {
|
||||
vi.spyOn(partyPokemon, "getAccuracyMultiplier");
|
||||
|
||||
enemyPokemon.summonData.battleStats[BattleStat.EVA] = 6;
|
||||
enemyPokemon.setStatStage(Stat.EVA, 6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE));
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SemiInvulnerableTag } from "#app/data/battler-tags.js";
|
||||
import { Type } from "#app/data/type.js";
|
||||
import { Biome } from "#app/enums/biome.js";
|
||||
@ -35,24 +35,24 @@ describe("Moves - Flower Shield", () => {
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("increases defense of all Grass-type Pokemon on the field by one stage - single battle", async () => {
|
||||
it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - single battle", async () => {
|
||||
game.override.enemySpecies(Species.CHERRIM);
|
||||
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
const cherrim = game.scene.getEnemyPokemon()!;
|
||||
const magikarp = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(magikarp.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(cherrim.getStatStage(Stat.DEF)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(magikarp.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(cherrim.getStatStage(Stat.DEF)).toBe(1);
|
||||
});
|
||||
|
||||
it("increases defense of all Grass-type Pokemon on the field by one stage - double battle", async () => {
|
||||
it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - double battle", async () => {
|
||||
game.override.enemySpecies(Species.MAGIKARP).startingBiome(Biome.GRASS).battleType("double");
|
||||
|
||||
await game.startBattle([Species.CHERRIM, Species.MAGIKARP]);
|
||||
@ -61,21 +61,21 @@ describe("Moves - Flower Shield", () => {
|
||||
const grassPokemons = field.filter(p => p.getTypes().includes(Type.GRASS));
|
||||
const nonGrassPokemons = field.filter(pokemon => !grassPokemons.includes(pokemon));
|
||||
|
||||
grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0));
|
||||
nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0));
|
||||
grassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0));
|
||||
nonGrassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0));
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(1));
|
||||
nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0));
|
||||
grassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(1));
|
||||
nonGrassPokemons.forEach(p => expect(p.getStatStage(Stat.DEF)).toBe(0));
|
||||
});
|
||||
|
||||
/**
|
||||
* See semi-vulnerable state tags. {@linkcode SemiInvulnerableTag}
|
||||
*/
|
||||
it("does not increase defense of a pokemon in semi-vulnerable state", async () => {
|
||||
it("does not raise DEF stat stage for a Pokemon in semi-vulnerable state", async () => {
|
||||
game.override.enemySpecies(Species.PARAS);
|
||||
game.override.enemyMoveset([Moves.DIG, Moves.DIG, Moves.DIG, Moves.DIG]);
|
||||
game.override.enemyLevel(50);
|
||||
@ -84,32 +84,32 @@ describe("Moves - Flower Shield", () => {
|
||||
const paras = game.scene.getEnemyPokemon()!;
|
||||
const cherrim = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(paras.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(cherrim.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(paras.getTag(SemiInvulnerableTag)).toBeUndefined;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(paras.getTag(SemiInvulnerableTag)).toBeDefined();
|
||||
expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(paras.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(cherrim.getStatStage(Stat.DEF)).toBe(1);
|
||||
});
|
||||
|
||||
it("does nothing if there are no Grass-type pokemon on the field", async () => {
|
||||
it("does nothing if there are no Grass-type Pokemon on the field", async () => {
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
|
||||
await game.startBattle([Species.MAGIKARP]);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
const ally = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(ally.getStatStage(Stat.DEF)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(ally.getStatStage(Stat.DEF)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -86,7 +86,7 @@ describe("Moves - Follow Me", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.FOLLOW_ME));
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
playerPokemon.sort((a, b) => a.getBattleStat(Stat.SPD) - b.getBattleStat(Stat.SPD));
|
||||
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
||||
|
||||
expect(playerPokemon[1].hp).toBeLessThan(playerStartingHp[1]);
|
||||
expect(playerPokemon[0].hp).toBe(playerStartingHp[0]);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { MoveEndPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnInitPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -11,7 +11,6 @@ import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||
import { allMoves } from "#app/data/move.js";
|
||||
|
||||
describe("Moves - Freezy Frost", () => {
|
||||
describe("integration tests", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
@ -39,44 +38,25 @@ describe("Moves - Freezy Frost", () => {
|
||||
game.override.ability(Abilities.NONE);
|
||||
});
|
||||
|
||||
it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, player uses Freezy Frost to clear all stat changes", { timeout: 10000 }, async () => {
|
||||
it("should clear all stat stage changes", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.RATTATA]);
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
const userAtkBefore = user.summonData.battleStats[BattleStat.ATK];
|
||||
const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK];
|
||||
expect(userAtkBefore).toBe(2);
|
||||
expect(enemyAtkBefore).toBe(-2);
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(2);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(-2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FREEZY_FROST));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
});
|
||||
|
||||
it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, enemy uses Freezy Frost to clear all stat changes", { timeout: 10000 }, async () => {
|
||||
game.override.enemyMoveset([Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST]);
|
||||
await game.startBattle([Species.SHUCKLE]); // Shuckle for slower Swords Dance on first turn so Freezy Frost doesn't affect it.
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
const userAtkBefore = user.summonData.battleStats[BattleStat.ATK];
|
||||
expect(userAtkBefore).toBe(2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
});
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -1,16 +1,13 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#app/data/pokemon-stat";
|
||||
import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase, TurnInitPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
|
||||
describe("Moves - Growth", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -28,37 +25,25 @@ describe("Moves - Growth", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.GROWTH;
|
||||
game.override.battleType("single");
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyAbility(Abilities.MOXIE);
|
||||
game.override.ability(Abilities.INSOMNIA);
|
||||
game.override.startingLevel(2000);
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
game.override.moveset([ Moves.GROWTH ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("GROWTH", async() => {
|
||||
const moveToUse = Moves.GROWTH;
|
||||
it("should raise SPATK stat stage by 1", async() => {
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA
|
||||
]);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[Stat.SPATK]).toBe(0);
|
||||
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.GROWTH));
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.SPATK]).toBe(1);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { MoveEndPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnInitPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
@ -37,44 +37,28 @@ describe("Moves - Haze", () => {
|
||||
game.override.ability(Abilities.NONE);
|
||||
});
|
||||
|
||||
it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, player uses Haze to clear all stat changes", { timeout: 10000 }, async () => {
|
||||
it("should reset all stat changes of all Pokemon on field", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.RATTATA]);
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
const userAtkBefore = user.summonData.battleStats[BattleStat.ATK];
|
||||
const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK];
|
||||
expect(userAtkBefore).toBe(2);
|
||||
expect(enemyAtkBefore).toBe(-2);
|
||||
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(2);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(-2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.HAZE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
});
|
||||
|
||||
it("Uses Swords Dance to raise own ATK by 2, Charm to lower enemy ATK by 2, enemy uses Haze to clear all stat changes", { timeout: 10000 }, async () => {
|
||||
game.override.enemyMoveset([Moves.HAZE, Moves.HAZE, Moves.HAZE, Moves.HAZE]);
|
||||
await game.startBattle([Species.SHUCKLE]); // Shuckle for slower Swords Dance on first turn so Haze doesn't affect it.
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
const userAtkBefore = user.summonData.battleStats[BattleStat.ATK];
|
||||
expect(userAtkBefore).toBe(2);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0);
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { MoveEndPhase, StatChangePhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
@ -36,17 +36,17 @@ describe("Moves - Make It Rain", () => {
|
||||
game.override.enemyLevel(100);
|
||||
});
|
||||
|
||||
it("should only reduce Sp. Atk. once in a double battle", async () => {
|
||||
it("should only lower SPATK stat stage by 1 once in a double battle", async () => {
|
||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should apply effects even if the target faints", async () => {
|
||||
@ -63,7 +63,7 @@ describe("Moves - Make It Rain", () => {
|
||||
await game.phaseInterceptor.to(StatChangePhase);
|
||||
|
||||
expect(enemyPokemon.isFainted()).toBe(true);
|
||||
expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should reduce Sp. Atk. once after KOing two enemies", async () => {
|
||||
@ -71,7 +71,7 @@ describe("Moves - Make It Rain", () => {
|
||||
|
||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN));
|
||||
@ -80,13 +80,13 @@ describe("Moves - Make It Rain", () => {
|
||||
await game.phaseInterceptor.to(StatChangePhase);
|
||||
|
||||
enemyPokemon.forEach(p => expect(p.isFainted()).toBe(true));
|
||||
expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should reduce Sp. Atk if it only hits the second target", async () => {
|
||||
it("should lower SPATK stat stage by 1 if it only hits the second target", async () => {
|
||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
@ -96,6 +96,6 @@ describe("Moves - Make It Rain", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
}, TIMEOUT);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TrappedTag } from "#app/data/battler-tags.js";
|
||||
import { CommandPhase, MoveEndPhase, TurnInitPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
@ -39,40 +39,40 @@ describe("Moves - Octolock", () => {
|
||||
game.override.ability(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("Reduces DEf and SPDEF by 1 each turn", { timeout: 10000 }, async () => {
|
||||
it("lowers DEF and SPDEF stat stages of the target Pokemon by 1 each turn", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.GRAPPLOCT]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// use Octolock and advance to init phase of next turn to check for stat changes
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-1);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-1);
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-1);
|
||||
|
||||
// take a second turn to make sure stat changes occur again
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-2);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-2);
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-2);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||
});
|
||||
|
||||
it("Traps the target pokemon", { timeout: 10000 }, async () => {
|
||||
it("traps the target Pokemon", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.GRAPPLOCT]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// before Octolock - enemy should not be trapped
|
||||
expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeUndefined();
|
||||
expect(enemyPokemon.findTag(t => t instanceof TrappedTag)).toBeUndefined();
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK));
|
||||
|
||||
// after Octolock - enemy should be trapped
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeDefined();
|
||||
expect(enemyPokemon.findTag(t => t instanceof TrappedTag)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StockpilingTag } from "#app/data/battler-tags.js";
|
||||
import { allMoves } from "#app/data/move.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
@ -16,6 +16,8 @@ describe("Moves - Spit Up", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
const spitUp = allMoves[Moves.SPIT_UP];
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||
});
|
||||
@ -34,8 +36,10 @@ describe("Moves - Spit Up", () => {
|
||||
game.override.enemyAbility(Abilities.NONE);
|
||||
game.override.enemyLevel(2000);
|
||||
|
||||
game.override.moveset([Moves.SPIT_UP, Moves.SPIT_UP, Moves.SPIT_UP, Moves.SPIT_UP]);
|
||||
game.override.moveset([ spitUp.id, spitUp.id, spitUp.id, spitUp.id ]);
|
||||
game.override.ability(Abilities.NONE);
|
||||
|
||||
vi.spyOn(spitUp, "calculateBattlePower");
|
||||
});
|
||||
|
||||
describe("consumes all stockpile stacks to deal damage (scaling with stacks)", () => {
|
||||
@ -52,13 +56,11 @@ describe("Moves - Spit Up", () => {
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup);
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
@ -77,13 +79,11 @@ describe("Moves - Spit Up", () => {
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup);
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
@ -103,13 +103,11 @@ describe("Moves - Spit Up", () => {
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup);
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(spitUp.calculateBattlePower).toHaveReturnedWith(expectedPower);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
@ -123,14 +121,12 @@ describe("Moves - Spit Up", () => {
|
||||
const stockpilingTag = pokemon.getTag(StockpilingTag)!;
|
||||
expect(stockpilingTag).toBeUndefined();
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.FAIL });
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).not.toHaveBeenCalled();
|
||||
expect(spitUp.calculateBattlePower).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("restores stat boosts granted by stacks", () => {
|
||||
@ -143,22 +139,20 @@ describe("Moves - Spit Up", () => {
|
||||
const stockpilingTag = pokemon.getTag(StockpilingTag)!;
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(MovePhase);
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
@ -174,26 +168,19 @@ describe("Moves - Spit Up", () => {
|
||||
|
||||
// for the sake of simplicity (and because other tests cover the setup), set boost amounts directly
|
||||
stockpilingTag.statChangeCounts = {
|
||||
[BattleStat.DEF]: -1,
|
||||
[BattleStat.SPDEF]: 2,
|
||||
[Stat.DEF]: -1,
|
||||
[Stat.SPDEF]: 2,
|
||||
};
|
||||
|
||||
expect(stockpilingTag.statChangeCounts).toMatchObject({
|
||||
[BattleStat.DEF]: -1,
|
||||
[BattleStat.SPDEF]: 2,
|
||||
});
|
||||
|
||||
vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower");
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
||||
|
||||
expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce();
|
||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(-2);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ describe("Moves - Spotlight", () => {
|
||||
* Spotlight will target the slower enemy. In this situation without Spotlight being used,
|
||||
* the faster enemy would normally end up with the Center of Attention tag.
|
||||
*/
|
||||
enemyPokemon.sort((a, b) => b.getBattleStat(Stat.SPD) - a.getBattleStat(Stat.SPD));
|
||||
enemyPokemon.sort((a, b) => b.getEffectiveStat(Stat.SPD) - a.getEffectiveStat(Stat.SPD));
|
||||
const spotTarget = enemyPokemon[1].getBattlerIndex();
|
||||
const attackTarget = enemyPokemon[0].getBattlerIndex();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StockpilingTag } from "#app/data/battler-tags.js";
|
||||
import { MoveResult, TurnMove } from "#app/field/pokemon.js";
|
||||
import { CommandPhase, TurnInitPhase } from "#app/phases";
|
||||
@ -38,7 +38,7 @@ describe("Moves - Stockpile", () => {
|
||||
game.override.ability(Abilities.NONE);
|
||||
});
|
||||
|
||||
it("Gains a stockpile stack and increases DEF and SPDEF by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => {
|
||||
it("gains a stockpile stack and raises user's DEF and SPDEF stat stages by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.ABOMASNOW]);
|
||||
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
@ -47,8 +47,8 @@ describe("Moves - Stockpile", () => {
|
||||
// we just have to know that they're implemented as a BattlerTag.
|
||||
|
||||
expect(user.getTag(StockpilingTag)).toBeUndefined();
|
||||
expect(user.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(0);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(0);
|
||||
|
||||
// use Stockpile four times
|
||||
for (let i = 0; i < 4; i++) {
|
||||
@ -60,18 +60,16 @@ describe("Moves - Stockpile", () => {
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
const stockpilingTag = user.getTag(StockpilingTag)!;
|
||||
const def = user.summonData.battleStats[BattleStat.DEF];
|
||||
const spdef = user.summonData.battleStats[BattleStat.SPDEF];
|
||||
|
||||
if (i < 3) { // first three uses should behave normally
|
||||
expect(def).toBe(i + 1);
|
||||
expect(spdef).toBe(i + 1);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(i + 1);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(i + 1);
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(i + 1);
|
||||
|
||||
} else { // fourth should have failed
|
||||
expect(def).toBe(3);
|
||||
expect(spdef).toBe(3);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(3);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(3);
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(3);
|
||||
expect(user.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ result: MoveResult.FAIL, move: Moves.STOCKPILE });
|
||||
@ -79,17 +77,17 @@ describe("Moves - Stockpile", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("Gains a stockpile stack even if DEF and SPDEF are at +6", { timeout: 10000 }, async () => {
|
||||
it("gains a stockpile stack even if user's DEF and SPDEF stat stages are at +6", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.ABOMASNOW]);
|
||||
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
|
||||
user.summonData.battleStats[BattleStat.DEF] = 6;
|
||||
user.summonData.battleStats[BattleStat.SPDEF] = 6;
|
||||
user.setStatStage(Stat.DEF, 6);
|
||||
user.setStatStage(Stat.SPDEF, 6);
|
||||
|
||||
expect(user.getTag(StockpilingTag)).toBeUndefined();
|
||||
expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6);
|
||||
expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(6);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(6);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
@ -97,8 +95,8 @@ describe("Moves - Stockpile", () => {
|
||||
const stockpilingTag = user.getTag(StockpilingTag)!;
|
||||
expect(stockpilingTag).toBeDefined();
|
||||
expect(stockpilingTag.stockpiledCount).toBe(1);
|
||||
expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6);
|
||||
expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(6);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(6);
|
||||
|
||||
// do it again, just for good measure
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
@ -109,8 +107,8 @@ describe("Moves - Stockpile", () => {
|
||||
const stockpilingTagAgain = user.getTag(StockpilingTag)!;
|
||||
expect(stockpilingTagAgain).toBeDefined();
|
||||
expect(stockpilingTagAgain.stockpiledCount).toBe(2);
|
||||
expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6);
|
||||
expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6);
|
||||
expect(user.getStatStage(Stat.DEF)).toBe(6);
|
||||
expect(user.getStatStage(Stat.SPDEF)).toBe(6);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StockpilingTag } from "#app/data/battler-tags.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
import { MoveResult, TurnMove } from "#app/field/pokemon.js";
|
||||
@ -137,7 +137,7 @@ describe("Moves - Swallow", () => {
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.FAIL });
|
||||
});
|
||||
|
||||
describe("restores stat boosts granted by stacks", () => {
|
||||
describe("restores stat stage boosts granted by stacks", () => {
|
||||
it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.ABOMASNOW]);
|
||||
|
||||
@ -150,20 +150,20 @@ describe("Moves - Swallow", () => {
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(MovePhase);
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(0);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(0);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => {
|
||||
it("lower stat stages based on stored values (different boosts)", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.ABOMASNOW]);
|
||||
|
||||
const pokemon = game.scene.getPlayerPokemon()!;
|
||||
@ -174,22 +174,17 @@ describe("Moves - Swallow", () => {
|
||||
|
||||
// for the sake of simplicity (and because other tests cover the setup), set boost amounts directly
|
||||
stockpilingTag.statChangeCounts = {
|
||||
[BattleStat.DEF]: -1,
|
||||
[BattleStat.SPDEF]: 2,
|
||||
[Stat.DEF]: -1,
|
||||
[Stat.SPDEF]: 2,
|
||||
};
|
||||
|
||||
expect(stockpilingTag.statChangeCounts).toMatchObject({
|
||||
[BattleStat.DEF]: -1,
|
||||
[BattleStat.SPDEF]: 2,
|
||||
});
|
||||
|
||||
game.doAttack(0);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
||||
|
||||
expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1);
|
||||
expect(pokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(-2);
|
||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||
|
||||
expect(pokemon.getTag(StockpilingTag)).toBeUndefined();
|
||||
});
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase, TurnInitPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
|
||||
|
||||
describe("Moves - Tail whip", () => {
|
||||
@ -33,29 +32,23 @@ describe("Moves - Tail whip", () => {
|
||||
game.override.enemyAbility(Abilities.INSOMNIA);
|
||||
game.override.ability(Abilities.INSOMNIA);
|
||||
game.override.startingLevel(2000);
|
||||
game.override.moveset([moveToUse]);
|
||||
game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
game.override.moveset([ moveToUse ]);
|
||||
game.override.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
it("TAIL_WHIP", async() => {
|
||||
it("should lower DEF stat stage by 1", async() => {
|
||||
const moveToUse = Moves.TAIL_WHIP;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.DEF]).toBe(0);
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.DEF]).toBe(-1);
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -39,16 +39,16 @@ describe("Abilities - Wind Rider", () => {
|
||||
const magikarpSpd = magikarp.getStat(Stat.SPD);
|
||||
const meowthSpd = meowth.getStat(Stat.SPD);
|
||||
|
||||
expect(magikarp.getBattleStat(Stat.SPD)).equal(magikarpSpd);
|
||||
expect(meowth.getBattleStat(Stat.SPD)).equal(meowthSpd);
|
||||
expect(magikarp.getEffectiveStat(Stat.SPD)).equal(magikarpSpd);
|
||||
expect(meowth.getEffectiveStat(Stat.SPD)).equal(meowthSpd);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(magikarp.getBattleStat(Stat.SPD)).toBe(magikarpSpd * 2);
|
||||
expect(meowth.getBattleStat(Stat.SPD)).toBe(meowthSpd * 2);
|
||||
expect(magikarp.getEffectiveStat(Stat.SPD)).toBe(magikarpSpd * 2);
|
||||
expect(meowth.getEffectiveStat(Stat.SPD)).toBe(meowthSpd * 2);
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined();
|
||||
});
|
||||
|
||||
@ -87,8 +87,8 @@ describe("Abilities - Wind Rider", () => {
|
||||
const enemySpd = enemy.getStat(Stat.SPD);
|
||||
|
||||
|
||||
expect(ally.getBattleStat(Stat.SPD)).equal(allySpd);
|
||||
expect(enemy.getBattleStat(Stat.SPD)).equal(enemySpd);
|
||||
expect(ally.getEffectiveStat(Stat.SPD)).equal(allySpd);
|
||||
expect(enemy.getEffectiveStat(Stat.SPD)).equal(enemySpd);
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined();
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined();
|
||||
|
||||
@ -96,8 +96,8 @@ describe("Abilities - Wind Rider", () => {
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(ally.getBattleStat(Stat.SPD)).toBe(allySpd * 2);
|
||||
expect(enemy.getBattleStat(Stat.SPD)).equal(enemySpd);
|
||||
expect(ally.getEffectiveStat(Stat.SPD)).toBe(allySpd * 2);
|
||||
expect(enemy.getEffectiveStat(Stat.SPD)).equal(enemySpd);
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined();
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined();
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
||||
import { MoveEndPhase, TurnEndPhase } from "#app/phases";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
@ -60,7 +60,6 @@ describe("Moves - Tidy Up", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(game.scene.arena.getTag(ArenaTagType.STEALTH_ROCK)).toBeUndefined();
|
||||
|
||||
}, 20000);
|
||||
|
||||
it("toxic spikes are cleared", async() => {
|
||||
@ -73,7 +72,6 @@ describe("Moves - Tidy Up", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(game.scene.arena.getTag(ArenaTagType.TOXIC_SPIKES)).toBeUndefined();
|
||||
|
||||
}, 20000);
|
||||
|
||||
it("sticky webs are cleared", async() => {
|
||||
@ -87,7 +85,6 @@ describe("Moves - Tidy Up", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(game.scene.arena.getTag(ArenaTagType.STICKY_WEB)).toBeUndefined();
|
||||
|
||||
}, 20000);
|
||||
|
||||
it.skip("substitutes are cleared", async() => {
|
||||
@ -101,22 +98,20 @@ describe("Moves - Tidy Up", () => {
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP));
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
// TODO: check for subs here once the move is implemented
|
||||
|
||||
}, 20000);
|
||||
|
||||
it("user's stats are raised with no traps set", async() => {
|
||||
await game.startBattle();
|
||||
const player = game.scene.getPlayerPokemon()!.summonData.battleStats;
|
||||
|
||||
expect(player[BattleStat.ATK]).toBe(0);
|
||||
expect(player[BattleStat.SPD]).toBe(0);
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(player[BattleStat.ATK]).toBe(+1);
|
||||
expect(player[BattleStat.SPD]).toBe(+1);
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
|
||||
}, 20000);
|
||||
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import { StatusEffect } from "../data/status-effect";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { Type, getTypeRgb } from "../data/type";
|
||||
import { getVariantTint } from "#app/data/variant";
|
||||
import { BattleStat } from "#app/data/battle-stat";
|
||||
import { Stat } from "#enums/stat";
|
||||
import BattleFlyout from "./battle-flyout";
|
||||
import { WindowVariant, addWindow } from "./ui-theme";
|
||||
import i18next from "i18next";
|
||||
@ -30,7 +30,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
private lastLevelExp: integer;
|
||||
private lastLevel: integer;
|
||||
private lastLevelCapped: boolean;
|
||||
private lastBattleStats: string;
|
||||
private lastStats: string;
|
||||
|
||||
private box: Phaser.GameObjects.Sprite;
|
||||
private nameText: Phaser.GameObjects.Text;
|
||||
@ -68,9 +68,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
|
||||
public flyoutMenu?: BattleFlyout;
|
||||
|
||||
private battleStatOrder: BattleStat[];
|
||||
private battleStatOrderPlayer = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD];
|
||||
private battleStatOrderEnemy = [BattleStat.HP, BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD];
|
||||
private statOrder: Stat[];
|
||||
private statOrderPlayer = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD ];
|
||||
private statOrderEnemy = [ Stat.HP, Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA, Stat.SPD ];
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) {
|
||||
super(scene, x, y);
|
||||
@ -229,9 +229,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5;
|
||||
const paddingX = this.player ? 4 : 2;
|
||||
const statOverflow = this.player ? 1 : 0;
|
||||
this.battleStatOrder = this.player ? this.battleStatOrderPlayer : this.battleStatOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
|
||||
this.statOrder = this.player ? this.statOrderPlayer : this.statOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order
|
||||
|
||||
this.battleStatOrder.map((s, i) => {
|
||||
this.statOrder.map((s, i) => {
|
||||
// we do a check for i > statOverflow to see when the stat labels go onto the next column
|
||||
// For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0
|
||||
// For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1
|
||||
@ -239,25 +239,25 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
|
||||
const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis
|
||||
let statY: number; // this will be the y-axis placement for the labels
|
||||
if (this.battleStatOrder[i] === BattleStat.SPD || this.battleStatOrder[i] === BattleStat.HP) {
|
||||
if (this.statOrder[i] === Stat.SPD || this.statOrder[i] === Stat.HP) {
|
||||
statY = baseY + 5;
|
||||
} else {
|
||||
statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us
|
||||
}
|
||||
|
||||
const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", BattleStat[s]);
|
||||
const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", Stat[s]);
|
||||
statLabel.setName("icon_stat_label_" + i.toString());
|
||||
statLabel.setOrigin(0, 0);
|
||||
statLabels.push(statLabel);
|
||||
this.statValuesContainer.add(statLabel);
|
||||
|
||||
const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.battleStatOrder[i] !== BattleStat.HP ? "3" : "empty");
|
||||
const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.statOrder[i] !== Stat.HP ? "3" : "empty");
|
||||
statNumber.setName("icon_stat_number_" + i.toString());
|
||||
statNumber.setOrigin(0, 0);
|
||||
this.statNumbers.push(statNumber);
|
||||
this.statValuesContainer.add(statNumber);
|
||||
|
||||
if (this.battleStatOrder[i] === BattleStat.HP) {
|
||||
if (this.statOrder[i] === Stat.HP) {
|
||||
statLabel.setVisible(false);
|
||||
statNumber.setVisible(false);
|
||||
}
|
||||
@ -433,10 +433,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
this.statValuesContainer.setPosition(8, 7);
|
||||
}
|
||||
|
||||
const battleStats = this.battleStatOrder.map(() => 0);
|
||||
const stats = this.statOrder.map(() => 0);
|
||||
|
||||
this.lastBattleStats = battleStats.join("");
|
||||
this.updateBattleStats(battleStats);
|
||||
this.lastStats = stats.join("");
|
||||
this.updateStats(stats);
|
||||
}
|
||||
|
||||
getTextureName(): string {
|
||||
@ -653,9 +653,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
const battleStats = pokemon.getStatStages();
|
||||
const battleStatsStr = battleStats.join("");
|
||||
|
||||
if (this.lastBattleStats !== battleStatsStr) {
|
||||
this.updateBattleStats(battleStats);
|
||||
this.lastBattleStats = battleStatsStr;
|
||||
if (this.lastStats !== battleStatsStr) {
|
||||
this.updateStats(battleStats);
|
||||
this.lastStats = battleStatsStr;
|
||||
}
|
||||
|
||||
this.shinyIcon.setVisible(pokemon.isShiny());
|
||||
@ -767,10 +767,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
updateBattleStats(battleStats: integer[]): void {
|
||||
this.battleStatOrder.map((s, i) => {
|
||||
if (s !== BattleStat.HP) {
|
||||
this.statNumbers[i].setFrame(battleStats[s].toString());
|
||||
updateStats(stats: integer[]): void {
|
||||
this.statOrder.map((s, i) => {
|
||||
if (s !== Stat.HP) {
|
||||
this.statNumbers[i].setFrame(stats[s].toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./t
|
||||
import { Mode } from "./ui";
|
||||
import * as Utils from "../utils";
|
||||
import MessageUiHandler from "./message-ui-handler";
|
||||
import { getStatName, Stat } from "../data/pokemon-stat";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
|
||||
import {Button} from "#enums/buttons";
|
||||
import i18next from "i18next";
|
||||
import { Stat, getStatKey, PERMANENT_STATS } from "#app/enums/stat.js";
|
||||
|
||||
export default class BattleMessageUiHandler extends MessageUiHandler {
|
||||
private levelUpStatsContainer: Phaser.GameObjects.Container;
|
||||
@ -98,9 +98,8 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
||||
const levelUpStatsLabelsContent = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 73, -94, "", TextStyle.WINDOW, { maxLines: 6 });
|
||||
let levelUpStatsLabelText = "";
|
||||
|
||||
const stats = Utils.getEnumValues(Stat);
|
||||
for (const s of stats) {
|
||||
levelUpStatsLabelText += `${getStatName(s)}\n`;
|
||||
for (const s of PERMANENT_STATS) {
|
||||
levelUpStatsLabelText += `${getStatKey(s)}\n`;
|
||||
}
|
||||
levelUpStatsLabelsContent.text = levelUpStatsLabelText;
|
||||
levelUpStatsLabelsContent.x -= levelUpStatsLabelsContent.displayWidth;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { Stat, getStatName } from "../data/pokemon-stat";
|
||||
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
|
||||
import { getStatKey, PERMANENT_STATS } from "#app/enums/stat.js";
|
||||
|
||||
const ivChartSize = 24;
|
||||
const ivChartStatCoordMultipliers = [[0, -1], [0.825, -0.5], [0.825, 0.5], [-0.825, -0.5], [-0.825, 0.5], [0, 1]];
|
||||
@ -53,16 +53,16 @@ export class StatsContainer extends Phaser.GameObjects.Container {
|
||||
|
||||
this.ivStatValueTexts = [];
|
||||
|
||||
new Array(6).fill(null).map((_, i: integer) => {
|
||||
const statLabel = addTextObject(this.scene, ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[i][0] * 1.325, ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[i][1] * 1.325 - 4 + ivLabelOffset[i], getStatName(i as Stat), TextStyle.TOOLTIP_CONTENT);
|
||||
for (const s of PERMANENT_STATS) {
|
||||
const statLabel = addTextObject(this.scene, ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[s][0] * 1.325, ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[s][1] * 1.325 - 4 + ivLabelOffset[s], getStatKey(s), TextStyle.TOOLTIP_CONTENT);
|
||||
statLabel.setOrigin(0.5);
|
||||
|
||||
this.ivStatValueTexts[i] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT);
|
||||
this.ivStatValueTexts[i].setOrigin(0.5);
|
||||
this.ivStatValueTexts[s] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, "0", TextStyle.TOOLTIP_CONTENT);
|
||||
this.ivStatValueTexts[s].setOrigin(0.5);
|
||||
|
||||
this.add(statLabel);
|
||||
this.add(this.ivStatValueTexts[i]);
|
||||
});
|
||||
this.add(this.ivStatValueTexts[s]);
|
||||
}
|
||||
}
|
||||
|
||||
updateIvs(ivs: integer[], originalIvs?: integer[]): void {
|
||||
|
@ -11,7 +11,6 @@ import Move, { MoveCategory } from "../data/move";
|
||||
import { getPokeballAtlasKey } from "../data/pokeball";
|
||||
import { getGenderColor, getGenderSymbol } from "../data/gender";
|
||||
import { getLevelRelExp, getLevelTotalExp } from "../data/exp";
|
||||
import { Stat, getStatName } from "../data/pokemon-stat";
|
||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||
import { StatusEffect } from "../data/status-effect";
|
||||
import { getBiomeName } from "../data/biomes";
|
||||
@ -23,6 +22,7 @@ import { Ability } from "../data/ability.js";
|
||||
import i18next from "i18next";
|
||||
import {modifierSortFunc} from "../modifier/modifier";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { Stat, getStatKey, PERMANENT_STATS } from "#app/enums/stat.js";
|
||||
|
||||
enum Page {
|
||||
PROFILE,
|
||||
@ -838,10 +838,8 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
const statsContainer = this.scene.add.container(0, -pageBg.height);
|
||||
pageContainer.add(statsContainer);
|
||||
|
||||
const stats = Utils.getEnumValues(Stat) as Stat[];
|
||||
|
||||
stats.forEach((stat, s) => {
|
||||
const statName = getStatName(stat);
|
||||
PERMANENT_STATS.forEach((stat, s) => {
|
||||
const statName = getStatKey(stat);
|
||||
const rowIndex = s % 3;
|
||||
const colIndex = Math.floor(s / 3);
|
||||
|
||||
@ -852,7 +850,7 @@ export default class SummaryUiHandler extends UiHandler {
|
||||
statsContainer.add(statLabel);
|
||||
|
||||
const statValueText = stat !== Stat.HP
|
||||
? Utils.formatStat(this.pokemon?.stats[s]!) // TODO: is this bang correct?
|
||||
? Utils.formatStat(this.pokemon?.getStat(stat)!) // TODO: is this bang correct?
|
||||
: `${Utils.formatStat(this.pokemon?.hp!, true)}/${Utils.formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct?
|
||||
|
||||
const statValue = addTextObject(this.scene, 120 + 88 * colIndex, 56 + 16 * rowIndex, statValueText, TextStyle.WINDOW_ALT);
|
||||
|
Loading…
Reference in New Issue
Block a user