[Move] Implement Power Shift (#4083)
* fully implement power shift * cleanup
This commit is contained in:
parent
80e347840d
commit
c59f6edf36
|
@ -5987,9 +5987,8 @@ export class SwapStatAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes the average of the user's and target's corresponding current
|
* Swaps the user's and target's corresponding current
|
||||||
* {@linkcode stat} values and sets that stat to the average for both
|
* {@linkcode EffectiveStat | stat} values
|
||||||
* temporarily.
|
|
||||||
* @param user the {@linkcode Pokemon} that used the move
|
* @param user the {@linkcode Pokemon} that used the move
|
||||||
* @param target the {@linkcode Pokemon} that the move was used on
|
* @param target the {@linkcode Pokemon} that the move was used on
|
||||||
* @param move N/A
|
* @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,
|
* Attribute used for status moves, namely Power Split and Guard Split,
|
||||||
* that take the average of a user's and target's corresponding
|
* 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)
|
new AttackMove(Moves.PSYSHIELD_BASH, Type.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, true),
|
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, true),
|
||||||
new SelfStatusMove(Moves.POWER_SHIFT, Type.NORMAL, -1, 10, -1, 0, 8)
|
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)
|
new AttackMove(Moves.STONE_AXE, Type.ROCK, MoveCategory.PHYSICAL, 65, 90, 15, 100, 0, 8)
|
||||||
.attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK)
|
.attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK)
|
||||||
.slicingMove(),
|
.slicingMove(),
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"switchedStat": "{{pokemonName}} switched {{stat}} with its target!",
|
"switchedStat": "{{pokemonName}} switched {{stat}} with its target!",
|
||||||
"sharedGuard": "{{pokemonName}} shared its guard with the target!",
|
"sharedGuard": "{{pokemonName}} shared its guard with the target!",
|
||||||
"sharedPower": "{{pokemonName}} shared its power 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!",
|
"goingAllOutForAttack": "{{pokemonName}} is going all out for this attack!",
|
||||||
"regainedHealth": "{{pokemonName}} regained\nhealth!",
|
"regainedHealth": "{{pokemonName}} regained\nhealth!",
|
||||||
"keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!",
|
"keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!",
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
Loading…
Reference in New Issue