mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-04-29 13:03:48 +01:00
[Bug][Move] Struggle no longer gets STAB (#5643)
* Struggle no longer gets STAB * Apply kev's suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
efad0d1324
commit
ae588ebff9
@ -4151,6 +4151,62 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return baseDamage;
|
return baseDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Determine the STAB multiplier for a move used against this pokemon.
|
||||||
|
*
|
||||||
|
* @param source - The attacking {@linkcode Pokemon}
|
||||||
|
* @param move - The {@linkcode Move} used in the attack
|
||||||
|
* @param ignoreSourceAbility - If `true`, ignores the attacking Pokemon's ability effects
|
||||||
|
* @param simulated - If `true`, suppresses changes to game state during the calculation
|
||||||
|
*
|
||||||
|
* @returns The STAB multiplier for the move used against this Pokemon
|
||||||
|
*/
|
||||||
|
calculateStabMultiplier(source: Pokemon, move: Move, ignoreSourceAbility: boolean, simulated: boolean): number {
|
||||||
|
// If the move has the Typeless attribute, it doesn't get STAB (e.g. struggle)
|
||||||
|
if (move.hasAttr(TypelessAttr)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const sourceTypes = source.getTypes();
|
||||||
|
const sourceTeraType = source.getTeraType();
|
||||||
|
const moveType = source.getMoveType(move);
|
||||||
|
const matchesSourceType = sourceTypes.includes(source.getMoveType(move));
|
||||||
|
const stabMultiplier = new NumberHolder(1);
|
||||||
|
if (matchesSourceType && moveType !== PokemonType.STELLAR) {
|
||||||
|
stabMultiplier.value += 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMoveAttrs(
|
||||||
|
CombinedPledgeStabBoostAttr,
|
||||||
|
source,
|
||||||
|
this,
|
||||||
|
move,
|
||||||
|
stabMultiplier,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!ignoreSourceAbility) {
|
||||||
|
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
source.isTerastallized &&
|
||||||
|
sourceTeraType === moveType &&
|
||||||
|
moveType !== PokemonType.STELLAR
|
||||||
|
) {
|
||||||
|
stabMultiplier.value += 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
source.isTerastallized &&
|
||||||
|
source.getTeraType() === PokemonType.STELLAR &&
|
||||||
|
(!source.stellarTypesBoosted.includes(moveType) ||
|
||||||
|
source.hasSpecies(Species.TERAPAGOS))
|
||||||
|
) {
|
||||||
|
stabMultiplier.value += matchesSourceType ? 0.5 : 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(stabMultiplier.value, 2.25);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the damage of an attack made by another Pokemon against this Pokemon
|
* Calculates the damage of an attack made by another Pokemon against this Pokemon
|
||||||
* @param source {@linkcode Pokemon} the attacking Pokemon
|
* @param source {@linkcode Pokemon} the attacking Pokemon
|
||||||
@ -4333,70 +4389,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
? 1
|
? 1
|
||||||
: this.randSeedIntRange(85, 100) / 100;
|
: this.randSeedIntRange(85, 100) / 100;
|
||||||
|
|
||||||
const sourceTypes = source.getTypes();
|
|
||||||
const sourceTeraType = source.getTeraType();
|
|
||||||
const matchesSourceType = sourceTypes.includes(moveType);
|
|
||||||
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
|
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
|
||||||
const stabMultiplier = new NumberHolder(1);
|
const stabMultiplier = this.calculateStabMultiplier(source, move, ignoreSourceAbility, simulated);
|
||||||
if (matchesSourceType && moveType !== PokemonType.STELLAR) {
|
|
||||||
stabMultiplier.value += 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoreSourceAbility) {
|
|
||||||
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
applyMoveAttrs(
|
|
||||||
CombinedPledgeStabBoostAttr,
|
|
||||||
source,
|
|
||||||
this,
|
|
||||||
move,
|
|
||||||
stabMultiplier,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
source.isTerastallized &&
|
|
||||||
sourceTeraType === moveType &&
|
|
||||||
moveType !== PokemonType.STELLAR
|
|
||||||
) {
|
|
||||||
stabMultiplier.value += 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
source.isTerastallized &&
|
|
||||||
source.getTeraType() === PokemonType.STELLAR &&
|
|
||||||
(!source.stellarTypesBoosted.includes(moveType) ||
|
|
||||||
source.hasSpecies(Species.TERAPAGOS))
|
|
||||||
) {
|
|
||||||
if (matchesSourceType) {
|
|
||||||
stabMultiplier.value += 0.5;
|
|
||||||
} else {
|
|
||||||
stabMultiplier.value += 0.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stabMultiplier.value = Math.min(stabMultiplier.value, 2.25);
|
|
||||||
|
|
||||||
/** Halves damage if the attacker is using a physical attack while burned */
|
/** Halves damage if the attacker is using a physical attack while burned */
|
||||||
const burnMultiplier = new NumberHolder(1);
|
let burnMultiplier = 1;
|
||||||
if (
|
if (
|
||||||
isPhysical &&
|
isPhysical &&
|
||||||
source.status &&
|
source.status &&
|
||||||
source.status.effect === StatusEffect.BURN
|
source.status.effect === StatusEffect.BURN &&
|
||||||
|
!move.hasAttr(BypassBurnDamageReductionAttr)
|
||||||
) {
|
) {
|
||||||
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
|
const burnDamageReductionCancelled = new BooleanHolder(false);
|
||||||
const burnDamageReductionCancelled = new BooleanHolder(false);
|
if (!ignoreSourceAbility) {
|
||||||
if (!ignoreSourceAbility) {
|
applyAbAttrs(
|
||||||
applyAbAttrs(
|
BypassBurnDamageReductionAbAttr,
|
||||||
BypassBurnDamageReductionAbAttr,
|
source,
|
||||||
source,
|
burnDamageReductionCancelled,
|
||||||
burnDamageReductionCancelled,
|
simulated,
|
||||||
simulated,
|
);
|
||||||
);
|
}
|
||||||
}
|
if (!burnDamageReductionCancelled.value) {
|
||||||
if (!burnDamageReductionCancelled.value) {
|
burnMultiplier = 0.5;
|
||||||
burnMultiplier.value = 0.5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4447,9 +4462,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
glaiveRushMultiplier.value *
|
glaiveRushMultiplier.value *
|
||||||
criticalMultiplier.value *
|
criticalMultiplier.value *
|
||||||
randomMultiplier *
|
randomMultiplier *
|
||||||
stabMultiplier.value *
|
stabMultiplier *
|
||||||
typeMultiplier *
|
typeMultiplier *
|
||||||
burnMultiplier.value *
|
burnMultiplier *
|
||||||
screenMultiplier.value *
|
screenMultiplier.value *
|
||||||
hitsTagMultiplier.value *
|
hitsTagMultiplier.value *
|
||||||
mistyTerrainMultiplier,
|
mistyTerrainMultiplier,
|
||||||
|
65
test/moves/struggle.test.ts
Normal file
65
test/moves/struggle.test.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Struggle", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.SPLASH])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not have its power boosted by adaptability or stab", async () => {
|
||||||
|
game.override.moveset([Moves.STRUGGLE]).ability(Abilities.ADAPTABILITY);
|
||||||
|
await game.classicMode.startBattle([Species.RATTATA]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
game.move.select(Moves.STRUGGLE);
|
||||||
|
|
||||||
|
const stabSpy = vi.spyOn(enemy, "calculateStabMultiplier");
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(stabSpy).toHaveReturnedWith(1);
|
||||||
|
|
||||||
|
stabSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ignore type effectiveness", async () => {
|
||||||
|
game.override.moveset([Moves.STRUGGLE]);
|
||||||
|
await game.classicMode.startBattle([Species.GASTLY]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
game.move.select(Moves.STRUGGLE);
|
||||||
|
|
||||||
|
const moveEffectivenessSpy = vi.spyOn(enemy, "getMoveEffectiveness");
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(moveEffectivenessSpy).toHaveReturnedWith(1);
|
||||||
|
|
||||||
|
moveEffectivenessSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user