implementation of rage fist

This commit is contained in:
geeil-han 2025-01-15 10:08:49 +01:00
parent d5f84cf3df
commit bb88fd6d11
6 changed files with 202 additions and 4 deletions

View File

@ -5,7 +5,8 @@ import type { Nature } from "#enums/nature";
/**
* Data that can customize a Pokemon in non-standard ways from its Species
* Currently only used by Mystery Encounters and Mints.
* Used by Mystery Encounters and Mints
* Also used as a counter how often a Pokemon got hit until new arena encounter
*/
export class CustomPokemonData {
public spriteScale: number;
@ -13,6 +14,8 @@ export class CustomPokemonData {
public passive: Abilities | -1;
public nature: Nature | -1;
public types: Type[];
//hitsRecivedCount aka hitsRecCount saves how often the pokemon got hit until a new arena encounter (used for Rage Fist)
public hitsRecCount: number;
constructor(data?: CustomPokemonData | Partial<CustomPokemonData>) {
if (!isNullOrUndefined(data)) {
@ -24,5 +27,10 @@ export class CustomPokemonData {
this.passive = this.passive ?? -1;
this.nature = this.nature ?? -1;
this.types = this.types ?? [];
this.hitsRecCount = this.hitsRecCount ?? 0;
}
resetHitRecivedCount(): void {
this.hitsRecCount = 0;
}
}

View File

@ -3993,12 +3993,34 @@ export class FriendshipPowerAttr extends VariablePowerAttr {
}
}
export class HitCountPowerAttr extends VariablePowerAttr {
/**
* This Attribute calculates the current power of {@linkcode Moves.RAGE_FIST}
* The counter for power calculation does not reset on every wave but on every new arena encounter
*/
export class RageFistPowerAttr extends VariablePowerAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
(args[0] as Utils.NumberHolder).value += Math.min(user.battleData.hitCount, 6) * 50;
const BDHitCount = user.battleData.hitCount;
const previousHitCount = user.battleData.prevHitCount;
this.updateHitRecivedCount(user, BDHitCount, previousHitCount);
console.log(`GHNote hitsRecCount: ${user.customPokemonData.hitsRecCount}`);
(args[0] as Utils.NumberHolder).value = 50 + (Math.min(user.customPokemonData.hitsRecCount, 6) * 50);
return true;
}
/**
* Updates the hitCount of recived hits during this arena encounter
* @param user Pokemon calling Rage Fist
* @param BDHitCount The hitCount of reviced hits this battle up until the function is called
* @param previousHitCount The hitCount of reviced hits this battle the last time Rage Fist was called
*/
updateHitRecivedCount(user: Pokemon, BDHitCount: number, previousHitCount: number): void {
user.customPokemonData.hitsRecCount += (BDHitCount - previousHitCount);
user.battleData.prevHitCount = BDHitCount;
}
}
/**
@ -11004,7 +11026,7 @@ export function initMoves() {
.attr(MultiHitAttr, MultiHitType._2),
new AttackMove(Moves.RAGE_FIST, Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9)
.partial() // Counter resets every wave instead of on arena reset
.attr(HitCountPowerAttr)
.attr(RageFistPowerAttr)
.punchingMove(),
new AttackMove(Moves.ARMOR_CANNON, Type.FIRE, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
.attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true),

View File

@ -5281,7 +5281,12 @@ export class PokemonSummonData {
}
export class PokemonBattleData {
//counts the hits the pokemon recived
public hitCount: number = 0;
/**
* used for {@linkcode Moves.RAGE_FIST} in order to save hit Counts recived before Rage Fist is applied
*/
public prevHitCount: number = 0;
public endured: boolean = false;
public berriesEaten: BerryType[] = [];
public abilitiesApplied: Abilities[] = [];

View File

@ -104,6 +104,12 @@ export class EncounterPhase extends BattlePhase {
}
if (!this.loaded) {
if (battle.battleType === BattleType.TRAINER) {
//resets hitRecCount during Trainer ecnounter
for (const pokemon of globalScene.getPlayerParty()) {
if (pokemon) {
pokemon.customPokemonData.resetHitRecivedCount();
}
}
battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here?
} else {
let enemySpecies = globalScene.randomSpecies(battle.waveIndex, level, true);

View File

@ -14,6 +14,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
for (const pokemon of globalScene.getPlayerParty()) {
if (pokemon) {
pokemon.resetBattleData();
pokemon.customPokemonData.resetHitRecivedCount();
}
}

View File

@ -0,0 +1,156 @@
import { BattlerIndex } from "#app/battle";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { allMoves } from "#app/data/move";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Moves - Rage Fist", () => {
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
.battleType("single")
.moveset([ Moves.RAGE_FIST, Moves.SPLASH, Moves.SUBSTITUTE ])
.startingLevel(100)
.enemyLevel(1)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.DOUBLE_KICK);
});
it("should have 100 more power if hit twice before calling Rage Fist", async () => {
game.override
.enemySpecies(Species.MAGIKARP);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("TurnEndPhase");
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
});
it("should maintain its power during next battle if it is within the same arena encounter", async () => {
game.override
.enemySpecies(Species.MAGIKARP)
.startingWave(1);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.toNextWave();
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(move.calculateBattlePower).toHaveLastReturnedWith(250);
});
it("should reset the hitRecCounter if we enter new trainer battle", async () => {
game.override
.enemySpecies(Species.MAGIKARP)
.startingWave(4);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.toNextWave();
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
});
it("should not increase the hitCounter if Substitute is hit", async () => {
game.override
.enemySpecies(Species.MAGIKARP)
.startingWave(4);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.SUBSTITUTE);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(game.scene.getPlayerPokemon()?.customPokemonData.hitsRecCount).toBe(0);
});
//For some unknown reason the second Rage fist is not called. This might be due to entering a new biome
it.todo("should reset the hitRecCounter if we enter new biome", async () => {
game.override
.enemySpecies(Species.MAGIKARP)
.startingWave(10);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.toNextWave();
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
});
//Test does not work correctly. Feel free to add changes if you can make it work
it.todo("should not reset the hitRecCounter if switched out", async () => {
game.override
.enemySpecies(Species.MAGIKARP)
.startingWave(1);
await game.classicMode.startBattle([ Species.CHARIZARD, Species.BLASTOISE ]);
const move = allMoves[Moves.RAGE_FIST];
vi.spyOn(move, "calculateBattlePower");
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
game.doSelectPartyPokemon(1);
await game.toNextWave();
game.move.select(Moves.RAGE_FIST);
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
game.doSelectPartyPokemon(0);
await game.phaseInterceptor.to("CommandPhase");
expect(move.calculateBattlePower).toHaveLastReturnedWith(150);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(Species.CHARIZARD);
});
});