Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Move] Fully implement Rage #3667

Open
wants to merge 23 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/data/battler-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,30 @@ export class ShellTrapTag extends BattlerTag {
}
}

export class RageTag extends BattlerTag {
ElizaAlex marked this conversation as resolved.
Show resolved Hide resolved
constructor() {
super(BattlerTagType.RAGE,[BattlerTagLapseType.MOVE_EFFECT],1,Moves.RAGE);
}
onAdd(pokemon: Pokemon) {
super.onAdd(pokemon);
/* This message might not exist on cartridge */
pokemon.scene.queueMessage(i18next.t("battlerTags:rageOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}));
}

lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.MOVE_EFFECT) {
return (pokemon.scene.getCurrentPhase() as MovePhase).move.getMove().id === Moves.RAGE;
} else if (lapseType === BattlerTagLapseType.CUSTOM) {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene,pokemon.getBattlerIndex(),true,[BattleStat.ATK],1,false));
ElizaAlex marked this conversation as resolved.
Show resolved Hide resolved
pokemon.scene.queueMessage(i18next.t("battlerTags:rageOnHit", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}));
return true;
}
return false;
}
}

export class TrappedTag extends BattlerTag {
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
super(tagType, lapseType, turnCount, sourceMove, sourceId);
Expand Down Expand Up @@ -1962,6 +1986,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
case BattlerTagType.GULP_MISSILE_ARROKUDA:
case BattlerTagType.GULP_MISSILE_PIKACHU:
return new GulpMissileTag(tagType, sourceMove);
case BattlerTagType.RAGE:
return new RageTag();
case BattlerTagType.NONE:
default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
Expand Down
2 changes: 1 addition & 1 deletion src/data/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6450,7 +6450,7 @@ export function initMoves() {
.attr(StatChangeAttr, BattleStat.SPD, 2, true),
new AttackMove(Moves.QUICK_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1),
new AttackMove(Moves.RAGE, Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1)
.partial(),
.attr(AddBattlerTagAttr,BattlerTagType.RAGE,true,false,0,0,false,true),
ElizaAlex marked this conversation as resolved.
Show resolved Hide resolved
new SelfStatusMove(Moves.TELEPORT, Type.PSYCHIC, -1, 20, -1, -6, 1)
.attr(ForceSwitchOutAttr, true)
.hidesUser(),
Expand Down
3 changes: 2 additions & 1 deletion src/enums/battler-tag-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ export enum BattlerTagType {
GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA",
GULP_MISSILE_PIKACHU = "GULP_MISSILE_PIKACHU",
BEAK_BLAST_CHARGING = "BEAK_BLAST_CHARGING",
SHELL_TRAP = "SHELL_TRAP"
SHELL_TRAP = "SHELL_TRAP",
RAGE = "RAGE"
}
2 changes: 2 additions & 0 deletions src/locales/en/battler-tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ export const battlerTags: SimpleTranslationEntries = {
"cursedOnAdd": "{{pokemonNameWithAffix}} cut its own HP and put a curse on the {{pokemonName}}!",
"cursedLapse": "{{pokemonNameWithAffix}} is afflicted by the Curse!",
"stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!",
"rageOnAdd":"{{pokemonNameWithAffix}}'s rage is starting to build",
ElizaAlex marked this conversation as resolved.
Show resolved Hide resolved
"rageOnHit": "{{pokemonNameWithAffix}}'s rage is building"
} as const;
3 changes: 3 additions & 0 deletions src/phases/move-effect-phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ export class MoveEffectPhase extends PokemonPhase {
if (move.category === MoveCategory.PHYSICAL && user.isPlayer() !== target.isPlayer()) {
target.lapseTag(BattlerTagType.SHELL_TRAP);
}
if (hitResult < HitResult.NO_EFFECT && move.category !== MoveCategory.STATUS) {
target.lapseTag(BattlerTagType.RAGE);
}
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
}
Expand Down
110 changes: 110 additions & 0 deletions src/test/moves/rage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "#test/utils/gameManager";
import { Species } from "#enums/species";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import {BattleStat} from "#app/data/battle-stat";

const TIMEOUT = 20 * 1000;

describe("Moves - Rage", () => {
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")
.ability(Abilities.UNNERVE)
.moveset([Moves.RAGE,Moves.SPLASH,Moves.SPORE])
.enemyAbility(Abilities.INSOMNIA)
.startingLevel(100)
.enemyLevel(100);
});

it(
"should raise attack if hit after use",
async () => {
game.override
.enemySpecies(Species.SHUCKLE)
.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
await game.startBattle([Species.NINJASK]);

const leadPokemon = game.scene.getPlayerPokemon()!;

// Ninjask uses rage, then gets hit, gets atk boost
game.doAttack(0);
await game.toNextTurn();
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(1);

}, TIMEOUT
);

it(
"should raise ATK if hit before using non-rage option",
async () => {
game.override
.enemySpecies(Species.NINJASK)
.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
await game.startBattle([Species.SHUCKLE]);

const leadPokemon = game.scene.getPlayerPokemon()!;

// Ninjask moves first, THEN shuckle uses rage, no ATK boost
game.doAttack(0);
await game.toNextTurn();
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);

// Shuckle Raged last turn, so when Ninjask hits it, ATK boost despite not using rage this turn
game.doAttack(1);
await game.toNextTurn();
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(1);
}, TIMEOUT
);

it(
"should not raise ATK if hit by status move",
async () => {
game.override
.enemySpecies(Species.NINJASK)
.enemyMoveset([Moves.RAGE, Moves.RAGE, Moves.RAGE, Moves.RAGE]);
await game.startBattle([Species.NINJASK]);

const leadPokemon = game.scene.getPlayerPokemon()!;

// Ninjask Rages, then slept. No boost.
game.doAttack(2);
await game.toNextTurn();
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);
}, TIMEOUT
);

it(
"should not raise ATK if rage has no effect",
async () => {
game.override
.enemySpecies(Species.GASTLY)
.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE])
.moveset([Moves.RAGE]);
await game.startBattle([Species.NINJASK]);

const leadPokemon = game.scene.getPlayerPokemon()!;

// Ninjask uses rage, but it has no effect, no ATK boost
game.doAttack(0);
await game.toNextTurn();
expect(leadPokemon.summonData.battleStats[BattleStat.ATK]).toBe(0);
}, TIMEOUT
);
});
Loading