[Move] Implement Power Shift (#4083)

* fully implement power shift

* cleanup
This commit is contained in:
Adrian T. 2024-09-10 00:54:31 +08:00 committed by GitHub
parent 80e347840d
commit c59f6edf36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 125 additions and 4 deletions

View File

@ -5987,9 +5987,8 @@ export class SwapStatAttr extends MoveEffectAttr {
}
/**
* Takes the average of the user's and target's corresponding current
* {@linkcode stat} values and sets that stat to the average for both
* temporarily.
* Swaps the user's and target's corresponding current
* {@linkcode EffectiveStat | stat} values
* @param user the {@linkcode Pokemon} that used the move
* @param target the {@linkcode Pokemon} that the move was used on
* @param move N/A
@ -6013,6 +6012,62 @@ export class SwapStatAttr extends MoveEffectAttr {
}
}
/**
* Attribute used to switch the user's own stats.
* Used by Power Shift.
* @extends MoveEffectAttr
*/
export class ShiftStatAttr extends MoveEffectAttr {
private statToSwitch: EffectiveStat;
private statToSwitchWith: EffectiveStat;
constructor(statToSwitch: EffectiveStat, statToSwitchWith: EffectiveStat) {
super();
this.statToSwitch = statToSwitch;
this.statToSwitchWith = statToSwitchWith;
}
/**
* Switches the user's stats based on the {@linkcode statToSwitch} and {@linkcode statToSwitchWith} attributes.
* @param {Pokemon} user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @param args n/a
* @returns whether the effect was applied
*/
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
const firstStat = user.getStat(this.statToSwitch, false);
const secondStat = user.getStat(this.statToSwitchWith, false);
user.setStat(this.statToSwitch, secondStat, false);
user.setStat(this.statToSwitchWith, firstStat, false);
user.scene.queueMessage(i18next.t("moveTriggers:shiftedStats", {
pokemonName: getPokemonNameWithAffix(user),
statToSwitch: i18next.t(getStatKey(this.statToSwitch)),
statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith))
}));
return true;
}
/**
* Encourages the user to use the move if the stat to switch with is greater than the stat to switch.
* @param {Pokemon} user the {@linkcode Pokemon} that used the move
* @param target n/a
* @param move n/a
* @returns number of points to add to the user's benefit score
*/
override getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
return user.getStat(this.statToSwitchWith, false) > user.getStat(this.statToSwitch, false) ? 10 : 0;
}
}
/**
* Attribute used for status moves, namely Power Split and Guard Split,
* that take the average of a user's and target's corresponding
@ -8894,7 +8949,8 @@ export function initMoves() {
new AttackMove(Moves.PSYSHIELD_BASH, Type.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, true),
new SelfStatusMove(Moves.POWER_SHIFT, Type.NORMAL, -1, 10, -1, 0, 8)
.unimplemented(),
.target(MoveTarget.USER)
.attr(ShiftStatAttr, Stat.ATK, Stat.DEF),
new AttackMove(Moves.STONE_AXE, Type.ROCK, MoveCategory.PHYSICAL, 65, 90, 15, 100, 0, 8)
.attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK)
.slicingMove(),

View File

@ -7,6 +7,7 @@
"switchedStat": "{{pokemonName}} switched {{stat}} with its target!",
"sharedGuard": "{{pokemonName}} shared its guard with the target!",
"sharedPower": "{{pokemonName}} shared its power with the target!",
"shiftedStats": "{{pokemonName}} switched its {{statToSwitch}} and {{statToSwitchWith}}!",
"goingAllOutForAttack": "{{pokemonName}} is going all out for this attack!",
"regainedHealth": "{{pokemonName}} regained\nhealth!",
"keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!",

View File

@ -0,0 +1,64 @@
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { Stat } from "#app/enums/stat";
import { Abilities } from "#enums/abilities";
import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Power Shift", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 20 * 1000;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.POWER_SHIFT, Moves.BULK_UP])
.battleType("single")
.ability(Abilities.BALL_FETCH)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY);
});
it("switches the user's raw Attack stat with its raw Defense stat", async () => {
await game.classicMode.startBattle([Species.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!;
playerPokemon.setStat(Stat.ATK, 10, false);
playerPokemon.setStat(Stat.DEF, 20, false);
game.move.select(Moves.BULK_UP);
await game.phaseInterceptor.to("TurnEndPhase");
// Stat stages are increased by 1
expect(playerPokemon.getStatStageMultiplier(Stat.ATK)).toBe(1.5);
expect(playerPokemon.getStatStageMultiplier(Stat.DEF)).toBe(1.5);
await game.toNextTurn();
game.move.select(Moves.POWER_SHIFT);
await game.phaseInterceptor.to("TurnEndPhase");
// Effective stats are calculated correctly
expect(playerPokemon.getEffectiveStat(Stat.ATK)).toBe(30);
expect(playerPokemon.getEffectiveStat(Stat.DEF)).toBe(15);
// Raw stats are swapped
expect(playerPokemon.getStat(Stat.ATK, false)).toBe(20);
expect(playerPokemon.getStat(Stat.DEF, false)).toBe(10);
}, TIMEOUT);
});