[Bug] Fix hard press bp and pp (#2441)
* Fix hard press bp and pp * Hard Press should have a max bp of 100 * Fix typo for acc * Add test
This commit is contained in:
parent
e21b91858b
commit
694616e1e9
|
@ -2961,9 +2961,29 @@ export class HpPowerAttr extends VariablePowerAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute used for moves whose base power scales with the opponent's HP
|
||||||
|
* Used for Crush Grip, Wring Out, and Hard Press
|
||||||
|
* maxBasePower 100 for Hard Press, 120 for others
|
||||||
|
*/
|
||||||
export class OpponentHighHpPowerAttr extends VariablePowerAttr {
|
export class OpponentHighHpPowerAttr extends VariablePowerAttr {
|
||||||
|
maxBasePower: number;
|
||||||
|
|
||||||
|
constructor(maxBasePower: number) {
|
||||||
|
super();
|
||||||
|
this.maxBasePower = maxBasePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the base power of the move to be the target's HP ratio times the maxBasePower with a min value of 1
|
||||||
|
* @param user n/a
|
||||||
|
* @param target the Pokemon being attacked
|
||||||
|
* @param move n/a
|
||||||
|
* @param args holds the base power of the move at args[0]
|
||||||
|
* @returns true
|
||||||
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(120 * target.getHpRatio()), 1);
|
(args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -6685,7 +6705,7 @@ export function initMoves() {
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new AttackMove(Moves.WRING_OUT, Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 4)
|
new AttackMove(Moves.WRING_OUT, Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 4)
|
||||||
.attr(OpponentHighHpPowerAttr)
|
.attr(OpponentHighHpPowerAttr, 120)
|
||||||
.makesContact(),
|
.makesContact(),
|
||||||
new SelfStatusMove(Moves.POWER_TRICK, Type.PSYCHIC, -1, 10, -1, 0, 4)
|
new SelfStatusMove(Moves.POWER_TRICK, Type.PSYCHIC, -1, 10, -1, 0, 4)
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
|
@ -6909,7 +6929,7 @@ export function initMoves() {
|
||||||
.triageMove()
|
.triageMove()
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new AttackMove(Moves.CRUSH_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
|
new AttackMove(Moves.CRUSH_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4)
|
||||||
.attr(OpponentHighHpPowerAttr),
|
.attr(OpponentHighHpPowerAttr, 120),
|
||||||
new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4)
|
new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4)
|
||||||
.attr(TrapAttr, BattlerTagType.MAGMA_STORM),
|
.attr(TrapAttr, BattlerTagType.MAGMA_STORM),
|
||||||
new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4)
|
new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4)
|
||||||
|
@ -8379,8 +8399,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.TACHYON_CUTTER, Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, -1, 0, 9)
|
new AttackMove(Moves.TACHYON_CUTTER, Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, -1, 0, 9)
|
||||||
.attr(MultiHitAttr, MultiHitType._2)
|
.attr(MultiHitAttr, MultiHitType._2)
|
||||||
.slicingMove(),
|
.slicingMove(),
|
||||||
new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
|
new AttackMove(Moves.HARD_PRESS, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9)
|
||||||
.attr(OpponentHighHpPowerAttr),
|
.attr(OpponentHighHpPowerAttr, 100),
|
||||||
new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9)
|
new StatusMove(Moves.DRAGON_CHEER, Type.DRAGON, -1, 15, -1, 0, 9)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true)
|
||||||
.target(MoveTarget.NEAR_ALLY)
|
.target(MoveTarget.NEAR_ALLY)
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import * as overrides from "#app/overrides";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import {
|
||||||
|
MoveEffectPhase,
|
||||||
|
} from "#app/phases";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { getMovePosition } from "#app/test/utils/gameManagerUtils";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { NumberHolder } from "#app/utils.js";
|
||||||
|
import Move from "#app/data/move.js";
|
||||||
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
|
import { allMoves, OpponentHighHpPowerAttr } from "#app/data/move.js";
|
||||||
|
|
||||||
|
describe("Moves - Hard Press", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX);
|
||||||
|
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||||
|
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.HARD_PRESS]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("power varies between 1 and 100, and is greater the more HP the target has", async () => {
|
||||||
|
await game.startBattle([Species.GRAVELER]);
|
||||||
|
const moveToBeUsed = allMoves[Moves.HARD_PRESS];
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToBeUsed));
|
||||||
|
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon();
|
||||||
|
const movePower = getMockedMovePower(enemy, game.scene.getPlayerPokemon(), moveToBeUsed);
|
||||||
|
const moveMaxBasePower = getMoveMaxBasePower(moveToBeUsed);
|
||||||
|
|
||||||
|
expect(movePower).toBe(moveMaxBasePower * enemy.getHpRatio());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the mocked move power based on the attributes of the move and the opponent's high HP.
|
||||||
|
*
|
||||||
|
* @param defender - The defending Pokémon.
|
||||||
|
* @param attacker - The attacking Pokémon.
|
||||||
|
* @param move - The move being used.
|
||||||
|
* @returns The calculated move power.
|
||||||
|
*/
|
||||||
|
const getMockedMovePower = (defender: Pokemon, attacker: Pokemon, move: Move) => {
|
||||||
|
const powerHolder = new NumberHolder(move.power);
|
||||||
|
|
||||||
|
if (move.hasAttr(OpponentHighHpPowerAttr)) {
|
||||||
|
const attr = move.getAttrs(OpponentHighHpPowerAttr);
|
||||||
|
attr[0].apply(attacker, defender, move, [ powerHolder ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return powerHolder.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the maximum base power of a move based on its attributes.
|
||||||
|
*
|
||||||
|
* @param move - The move which maximum base power is being retrieved.
|
||||||
|
* @returns The maximum base power of the move.
|
||||||
|
*/
|
||||||
|
const getMoveMaxBasePower = (move: Move) => {
|
||||||
|
const attr = move.getAttrs(OpponentHighHpPowerAttr);
|
||||||
|
|
||||||
|
return (attr[0] as OpponentHighHpPowerAttr)["maxBasePower"];
|
||||||
|
};
|
Loading…
Reference in New Issue