mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-15 12:52:20 +02:00
Refactor parameter list for pokemon#getBaseDamage and pokemon#getAttackDamage
This commit is contained in:
parent
a1cb77a8da
commit
607dc70b9b
@ -4811,8 +4811,8 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const category = (args[0] as NumberHolder);
|
||||
|
||||
const predictedPhysDmg = target.getBaseDamage(user, move, MoveCategory.PHYSICAL, true, true, true, true);
|
||||
const predictedSpecDmg = target.getBaseDamage(user, move, MoveCategory.SPECIAL, true, true, true, true);
|
||||
const predictedPhysDmg = target.getBaseDamage({source: user, move, moveCategory: MoveCategory.PHYSICAL, ignoreAbility: true, ignoreSourceAbility: true, ignoreAllyAbility: true, ignoreSourceAllyAbility: true, simulated: true});
|
||||
const predictedSpecDmg = target.getBaseDamage({source: user, move, moveCategory: MoveCategory.SPECIAL, ignoreAbility: true, ignoreSourceAbility: true, ignoreAllyAbility: true, ignoreSourceAllyAbility: true, simulated: true});
|
||||
|
||||
if (predictedPhysDmg > predictedSpecDmg) {
|
||||
category.value = MoveCategory.PHYSICAL;
|
||||
|
@ -277,6 +277,36 @@ export enum FieldPosition {
|
||||
RIGHT,
|
||||
}
|
||||
|
||||
/** Base typeclass for damage parameter methods, used for DRY */
|
||||
type damageParams = {
|
||||
/** The attacking {@linkcode Pokemon} */
|
||||
source: Pokemon;
|
||||
/** The move used in the attack */
|
||||
move: Move;
|
||||
/** The move's {@linkcode MoveCategory} after variable-category effects are applied */
|
||||
moveCategory: MoveCategory;
|
||||
/** If `true`, ignores this Pokemon's defensive ability effects */
|
||||
ignoreAbility?: boolean;
|
||||
/** If `true`, ignores the attacking Pokemon's ability effects */
|
||||
ignoreSourceAbility?: boolean;
|
||||
/** If `true`, ignores the ally Pokemon's ability effects */
|
||||
ignoreAllyAbility?: boolean;
|
||||
/** If `true`, ignores the ability effects of the attacking pokemon's ally */
|
||||
ignoreSourceAllyAbility?: boolean;
|
||||
/** If `true`, calculates damage for a critical hit */
|
||||
isCritical?: boolean;
|
||||
/** If `true`, suppresses changes to game state during the calculation */
|
||||
simulated?: boolean;
|
||||
/** If defined, used in place of calculated effectiveness values */
|
||||
effectiveness?: number;
|
||||
}
|
||||
|
||||
/** Type for the parameters of {@linkcode Pokemon#getBaseDamage getBaseDamage} */
|
||||
type getBaseDamageParams = Omit<damageParams, "effectiveness">
|
||||
|
||||
/** Type for the parameters of {@linkcode Pokemon#getAttackDamage getAttackDamage} */
|
||||
type getAttackDamageParams = Omit<damageParams, "moveCategory">;
|
||||
|
||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public id: number;
|
||||
public name: string;
|
||||
@ -1441,25 +1471,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* Calculate the critical-hit stage of a move used against this pokemon by
|
||||
* the given source
|
||||
*
|
||||
* @param source the {@linkcode Pokemon} who using the move
|
||||
* @param move the {@linkcode Move} being used
|
||||
* @returns the final critical-hit stage value
|
||||
* @param source - The {@linkcode Pokemon} who using the move
|
||||
* @param move - The {@linkcode Move} being used
|
||||
* @returns The final critical-hit stage value
|
||||
*/
|
||||
getCritStage(source: Pokemon, move: Move): number {
|
||||
const critStage = new NumberHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critStage);
|
||||
globalScene.applyModifiers(
|
||||
CritBoosterModifier,
|
||||
source.isPlayer(),
|
||||
source,
|
||||
critStage,
|
||||
);
|
||||
globalScene.applyModifiers(
|
||||
TempCritBoosterModifier,
|
||||
source.isPlayer(),
|
||||
critStage,
|
||||
);
|
||||
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage)
|
||||
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage);
|
||||
const critBoostTag = source.getTag(CritBoostTag);
|
||||
if (critBoostTag) {
|
||||
if (critBoostTag instanceof DragonCheerTag) {
|
||||
@ -1475,6 +1496,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return critStage.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the category of a move when used by this pokemon after
|
||||
* category-changing move effects are applied.
|
||||
* @param target - The {@linkcode Pokemon} using the move
|
||||
* @param move - The {@linkcode Move} being used
|
||||
* @returns The given move's final category
|
||||
*/
|
||||
getMoveCategory(target: Pokemon, move: Move): MoveCategory {
|
||||
const moveCategory = new Utils.NumberHolder(move.category);
|
||||
applyMoveAttrs(VariableMoveCategoryAttr, this, target, move, moveCategory);
|
||||
return moveCategory.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and retrieves the final value of a stat considering any held
|
||||
* items, move effects, opponent abilities, and whether there was a critical
|
||||
@ -4075,27 +4109,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
/**
|
||||
* Calculates the base damage of the given move against this Pokemon when attacked by the given source.
|
||||
* Used during damage calculation and for Shell Side Arm's forecasting effect.
|
||||
* @param source the attacking {@linkcode Pokemon}.
|
||||
* @param move the {@linkcode Move} used in the attack.
|
||||
* @param moveCategory the move's {@linkcode MoveCategory} after variable-category effects are applied.
|
||||
* @param ignoreAbility if `true`, ignores this Pokemon's defensive ability effects (defaults to `false`).
|
||||
* @param ignoreSourceAbility if `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`).
|
||||
* @param ignoreAllyAbility if `true`, ignores the ally Pokemon's ability effects (defaults to `false`).
|
||||
* @param ignoreSourceAllyAbility if `true`, ignores the attacking Pokemon's ally's ability effects (defaults to `false`).
|
||||
* @param isCritical if `true`, calculates effective stats as if the hit were critical (defaults to `false`).
|
||||
* @param simulated if `true`, suppresses changes to game state during calculation (defaults to `true`).
|
||||
* @param source - The attacking {@linkcode Pokemon}.
|
||||
* @param move - The {@linkcode Move} used in the attack.
|
||||
* @param moveCategory - The move's {@linkcode MoveCategory} after variable-category effects are applied.
|
||||
* @param ignoreAbility - If `true`, ignores this Pokemon's defensive ability effects (defaults to `false`).
|
||||
* @param ignoreSourceAbility - If `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`).
|
||||
* @param ignoreAllyAbility - If `true`, ignores the ally Pokemon's ability effects (defaults to `false`).
|
||||
* @param ignoreSourceAllyAbility - If `true`, ignores the attacking Pokemon's ally's ability effects (defaults to `false`).
|
||||
* @param isCritical - if `true`, calculates effective stats as if the hit were critical (defaults to `false`).
|
||||
* @param simulated - if `true`, suppresses changes to game state during calculation (defaults to `true`).
|
||||
* @returns The move's base damage against this Pokemon when used by the source Pokemon.
|
||||
*/
|
||||
getBaseDamage(
|
||||
source: Pokemon,
|
||||
move: Move,
|
||||
moveCategory: MoveCategory,
|
||||
{
|
||||
source,
|
||||
move,
|
||||
moveCategory,
|
||||
ignoreAbility = false,
|
||||
ignoreSourceAbility = false,
|
||||
ignoreAllyAbility = false,
|
||||
ignoreSourceAllyAbility = false,
|
||||
isCritical = false,
|
||||
simulated = true,
|
||||
simulated = true}: getBaseDamageParams
|
||||
): number {
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
|
||||
@ -4222,27 +4257,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
/**
|
||||
* Calculates the damage of an attack made by another Pokemon against this Pokemon
|
||||
* @param source {@linkcode Pokemon} the attacking Pokemon
|
||||
* @param move {@linkcode Pokemon} the move used in the attack
|
||||
* @param move The {@linkcode Move} used in the attack
|
||||
* @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects
|
||||
* @param ignoreSourceAbility If `true`, ignores the attacking Pokemon's ability effects
|
||||
* @param ignoreAllyAbility If `true`, ignores the ally Pokemon's ability effects
|
||||
* @param ignoreSourceAllyAbility If `true`, ignores the ability effects of the attacking pokemon's ally
|
||||
* @param isCritical If `true`, calculates damage for a critical hit.
|
||||
* @param simulated If `true`, suppresses changes to game state during the calculation.
|
||||
* @returns a {@linkcode DamageCalculationResult} object with three fields:
|
||||
* - `cancelled`: `true` if the move was cancelled by another effect.
|
||||
* - `result`: {@linkcode HitResult} indicates the attack's type effectiveness.
|
||||
* - `damage`: `number` the attack's final damage output.
|
||||
* @param effectiveness If defined, used in place of calculated effectiveness values
|
||||
* @returns The {@linkcode DamageCalculationResult}
|
||||
*/
|
||||
getAttackDamage(
|
||||
source: Pokemon,
|
||||
move: Move,
|
||||
{
|
||||
source,
|
||||
move,
|
||||
ignoreAbility = false,
|
||||
ignoreSourceAbility = false,
|
||||
ignoreAllyAbility = false,
|
||||
ignoreSourceAllyAbility = false,
|
||||
isCritical = false,
|
||||
simulated = true,
|
||||
effectiveness}: getAttackDamageParams,
|
||||
): DamageCalculationResult {
|
||||
const damage = new NumberHolder(0);
|
||||
const defendingSide = this.isPlayer()
|
||||
@ -4272,7 +4307,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
*
|
||||
* Note that the source's abilities are not ignored here
|
||||
*/
|
||||
const typeMultiplier = this.getMoveEffectiveness(
|
||||
const typeMultiplier = effectiveness ?? this.getMoveEffectiveness(
|
||||
source,
|
||||
move,
|
||||
ignoreAbility,
|
||||
@ -4344,7 +4379,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* The attack's base damage, as determined by the source's level, move power
|
||||
* and Attack stat as well as this Pokemon's Defense stat
|
||||
*/
|
||||
const baseDamage = this.getBaseDamage(
|
||||
const baseDamage = this.getBaseDamage({
|
||||
source,
|
||||
move,
|
||||
moveCategory,
|
||||
@ -4354,7 +4389,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
ignoreSourceAllyAbility,
|
||||
isCritical,
|
||||
simulated,
|
||||
);
|
||||
});
|
||||
|
||||
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
|
||||
const { targets, multiple } = getMoveTargets(source, move.id);
|
||||
@ -4637,7 +4672,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
cancelled,
|
||||
result,
|
||||
damage: dmg,
|
||||
} = this.getAttackDamage(source, move, false, false, false, false, isCritical, false);
|
||||
} = this.getAttackDamage(
|
||||
{source, move, isCritical, simulated: false});
|
||||
|
||||
const typeBoost = source.findTag(
|
||||
t =>
|
||||
@ -7311,14 +7347,15 @@ export class EnemyPokemon extends Pokemon {
|
||||
].includes(move.id);
|
||||
return (
|
||||
doesNotFail &&
|
||||
p.getAttackDamage(
|
||||
this,
|
||||
p.getAttackDamage({
|
||||
source: this,
|
||||
move,
|
||||
!p.battleData.abilityRevealed,
|
||||
false,
|
||||
!p.getAlly()?.battleData.abilityRevealed,
|
||||
false,
|
||||
ignoreAbility: !p.battleData.abilityRevealed,
|
||||
ignoreSourceAbility: false,
|
||||
ignoreAllyAbility: !p.getAlly()?.battleData.abilityRevealed,
|
||||
ignoreSourceAllyAbility: false,
|
||||
isCritical,
|
||||
}
|
||||
).damage >= p.hp
|
||||
);
|
||||
})
|
||||
|
@ -50,7 +50,11 @@ describe("Moves - Friend Guard", () => {
|
||||
// Get the last return value from `getAttackDamage`
|
||||
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
|
||||
// Making sure the test is controlled; turn 1 damage is equal to base damage (after rounding)
|
||||
expect(turn1Damage).toBe(Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL)));
|
||||
expect(turn1Damage).toBe(
|
||||
Math.floor(
|
||||
player1.getBaseDamage({ source: enemy1, move: allMoves[Moves.TACKLE], moveCategory: MoveCategory.PHYSICAL }),
|
||||
),
|
||||
);
|
||||
|
||||
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[Abilities.FRIEND_GUARD]);
|
||||
|
||||
@ -64,7 +68,10 @@ describe("Moves - Friend Guard", () => {
|
||||
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
|
||||
// With the ally's Friend Guard, damage should have been reduced from base damage by 25%
|
||||
expect(turn2Damage).toBe(
|
||||
Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL) * 0.75),
|
||||
Math.floor(
|
||||
player1.getBaseDamage({ source: enemy1, move: allMoves[Moves.TACKLE], moveCategory: MoveCategory.PHYSICAL }) *
|
||||
0.75,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -61,11 +61,11 @@ describe("Abilities - Infiltrator", () => {
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
const preScreenDmg = enemy.getAttackDamage(player, allMoves[move]).damage;
|
||||
const preScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage;
|
||||
|
||||
game.scene.arena.addTag(tagType, 1, Moves.NONE, enemy.id, ArenaTagSide.ENEMY, true);
|
||||
|
||||
const postScreenDmg = enemy.getAttackDamage(player, allMoves[move]).damage;
|
||||
const postScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage;
|
||||
|
||||
expect(postScreenDmg).toBe(preScreenDmg);
|
||||
expect(player.battleData.abilitiesApplied[0]).toBe(Abilities.INFILTRATOR);
|
||||
|
@ -47,7 +47,9 @@ describe("Battle Mechanics - Damage Calculation", () => {
|
||||
|
||||
// expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2
|
||||
// = 31.8666...
|
||||
expect(enemyPokemon.getAttackDamage(playerPokemon, allMoves[Moves.TACKLE]).damage).toBeCloseTo(31);
|
||||
expect(enemyPokemon.getAttackDamage({ source: playerPokemon, move: allMoves[Moves.TACKLE] }).damage).toBeCloseTo(
|
||||
31,
|
||||
);
|
||||
});
|
||||
|
||||
it("Attacks deal 1 damage at minimum", async () => {
|
||||
@ -91,7 +93,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
|
||||
const magikarp = game.scene.getPlayerPokemon()!;
|
||||
const dragonite = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(dragonite.getAttackDamage(magikarp, allMoves[Moves.DRAGON_RAGE]).damage).toBe(40);
|
||||
expect(dragonite.getAttackDamage({ source: magikarp, move: allMoves[Moves.DRAGON_RAGE] }).damage).toBe(40);
|
||||
});
|
||||
|
||||
it("One-hit KO moves ignore damage multipliers", async () => {
|
||||
@ -102,7 +104,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
|
||||
const magikarp = game.scene.getPlayerPokemon()!;
|
||||
const aggron = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(aggron.getAttackDamage(magikarp, allMoves[Moves.FISSURE]).damage).toBe(aggron.hp);
|
||||
expect(aggron.getAttackDamage({ source: magikarp, move: allMoves[Moves.FISSURE] }).damage).toBe(aggron.hp);
|
||||
});
|
||||
|
||||
it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => {
|
||||
|
@ -97,14 +97,20 @@ describe("Moves - Dig", () => {
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
const preDigEarthquakeDmg = playerPokemon.getAttackDamage(enemyPokemon, allMoves[Moves.EARTHQUAKE]).damage;
|
||||
const preDigEarthquakeDmg = playerPokemon.getAttackDamage({
|
||||
source: enemyPokemon,
|
||||
move: allMoves[Moves.EARTHQUAKE],
|
||||
}).damage;
|
||||
|
||||
game.move.select(Moves.DIG);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
|
||||
const postDigEarthquakeDmg = playerPokemon.getAttackDamage(enemyPokemon, allMoves[Moves.EARTHQUAKE]).damage;
|
||||
const postDigEarthquakeDmg = playerPokemon.getAttackDamage({
|
||||
source: enemyPokemon,
|
||||
move: allMoves[Moves.EARTHQUAKE],
|
||||
}).damage;
|
||||
// these hopefully get avoid rounding errors :shrug:
|
||||
expect(postDigEarthquakeDmg).toBeGreaterThanOrEqual(2 * preDigEarthquakeDmg);
|
||||
expect(postDigEarthquakeDmg).toBeLessThan(2 * (preDigEarthquakeDmg + 1));
|
||||
|
@ -71,7 +71,7 @@ describe("Moves - Spectral Thief", () => {
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
const moveToCheck = allMoves[Moves.SPECTRAL_THIEF];
|
||||
const dmgBefore = enemy.getAttackDamage(player, moveToCheck, false, false, false, false).damage;
|
||||
const dmgBefore = enemy.getAttackDamage({ source: player, move: moveToCheck }).damage;
|
||||
|
||||
enemy.setStatStage(Stat.ATK, 6);
|
||||
|
||||
@ -80,7 +80,7 @@ describe("Moves - Spectral Thief", () => {
|
||||
game.move.select(Moves.SPECTRAL_THIEF);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(dmgBefore).toBeLessThan(enemy.getAttackDamage(player, moveToCheck, false, false, false, false).damage);
|
||||
expect(dmgBefore).toBeLessThan(enemy.getAttackDamage({ source: player, move: moveToCheck }).damage);
|
||||
});
|
||||
|
||||
it("should steal stat stages as a negative value with Contrary.", async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user