[Bug] Fix when variable move power is called (#6126)

* Apply variable power attribute before type boost

* Update test/abilities/normal-move-type-change.test.ts

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>

* Minor test improvements

---------

Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
Acelynn Zhang 2025-07-25 01:16:23 -05:00 committed by GitHub
parent 99545cf3c7
commit fc128a2f4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 44 additions and 7 deletions

View File

@ -808,16 +808,14 @@ export abstract class Move implements Localizable {
}
const power = new NumberHolder(this.power);
applyMoveAttrs("VariablePowerAttr", source, target, this, power);
const typeChangeMovePowerMultiplier = new NumberHolder(1);
const typeChangeHolder = new NumberHolder(this.type);
applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier});
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60;
}
const abAttrParams: PreAttackModifyPowerAbAttrParams = {
pokemon: source,
opponent: target,
@ -832,6 +830,13 @@ export abstract class Move implements Localizable {
applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally});
}
// Non-priority, single-hit moves of the user's Tera Type are always a bare minimum of 60 power
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
power.value = 60;
}
const fieldAuras = new Set(
globalScene.getField(true)
.map((p) => p.getAbilityAttrs("FieldMoveTypePowerBoostAbAttr").filter(attr => {
@ -855,7 +860,6 @@ export abstract class Move implements Localizable {
power.value *= typeBoost.boostValue;
}
applyMoveAttrs("VariablePowerAttr", source, target, this, power);
if (!this.hasAttr("TypelessAttr")) {
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);

View File

@ -48,7 +48,7 @@ describe.each([
.startingLevel(100)
.starterSpecies(SpeciesId.MAGIKARP)
.ability(ab)
.moveset([MoveId.TACKLE, MoveId.REVELATION_DANCE, MoveId.FURY_SWIPES])
.moveset([MoveId.TACKLE, MoveId.REVELATION_DANCE, MoveId.FURY_SWIPES, MoveId.CRUSH_GRIP])
.enemySpecies(SpeciesId.DUSCLOPS)
.enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset(MoveId.SPLASH)
@ -75,6 +75,27 @@ describe.each([
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
});
// Regression test to ensure proper ordering of effects
it("should still boost variable-power moves", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.field.getPlayerPokemon();
const typeSpy = vi.spyOn(playerPokemon, "getMoveType");
const enemyPokemon = game.field.getEnemyPokemon();
const enemySpy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
const powerSpy = vi.spyOn(allMoves[MoveId.CRUSH_GRIP], "calculateBattlePower");
game.move.select(MoveId.CRUSH_GRIP);
await game.toEndOfTurn();
expect(typeSpy).toHaveLastReturnedWith(ty);
expect(enemySpy).toHaveReturnedWith(1);
expect(powerSpy).toHaveReturnedWith(144); // 120 * 1.2
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
});
// Galvanize specifically would like to check for volt absorb's activation
if (ab === AbilityId.GALVANIZE) {
it("should cause Normal-type attacks to activate Volt Absorb", async () => {

View File

@ -44,6 +44,18 @@ describe("Abilities - Normalize", () => {
expect(powerSpy).toHaveLastReturnedWith(toDmgValue(allMoves[MoveId.TACKLE].power * 1.2));
});
it("should boost variable power moves", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const magikarp = game.field.getPlayerPokemon();
magikarp.friendship = 255;
const powerSpy = vi.spyOn(allMoves[MoveId.RETURN], "calculateBattlePower");
game.move.use(MoveId.RETURN);
await game.toEndOfTurn();
expect(powerSpy).toHaveLastReturnedWith(102 * 1.2);
});
it("should not apply the old type boost item after changing a move's type", async () => {
game.override
.startingHeldItems([{ name: "ATTACK_TYPE_BOOSTER", count: 1, type: PokemonType.GRASS }])