[Move] Fully Implement Plasma Fists (#4446)

* Implement Plasma Fists

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

* Update arena-tag.json

---------

Co-authored-by: Lugiad <adrien.grivel@hotmail.fr>
This commit is contained in:
innerthunder 2024-09-29 19:30:33 -07:00 committed by GitHub
parent d8ee8ad821
commit 1bae87fa56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 154 additions and 8 deletions

View File

@ -511,6 +511,39 @@ class WaterSportTag extends WeakenMoveTypeTag {
}
}
/**
* Arena Tag class for the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}.
* Converts Normal-type moves to Electric type for the rest of the turn.
*/
export class PlasmaFistsTag extends ArenaTag {
constructor() {
super(ArenaTagType.PLASMA_FISTS, 1, Moves.PLASMA_FISTS);
}
/** Queues Plasma Fists' on-add message */
onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
}
onRemove(arena: Arena): void { } // Removes default on-remove message
/**
* Converts Normal-type moves to Electric type
* @param arena n/a
* @param args
* - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type}
* @returns `true` if the given move type changed; `false` otherwise.
*/
apply(arena: Arena, args: any[]): boolean {
const moveType = args[0];
if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) {
moveType.value = Type.ELECTRIC;
return true;
}
return false;
}
}
/**
* Abstract class to implement arena traps.
*/
@ -1010,6 +1043,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT:
return new WaterSportTag(turnCount, sourceId);
case ArenaTagType.PLASMA_FISTS:
return new PlasmaFistsTag();
case ArenaTagType.SPIKES:
return new SpikesTag(sourceId, side);
case ArenaTagType.TOXIC_SPIKES:

View File

@ -8906,8 +8906,8 @@ export function initMoves() {
.attr(HalfSacrificialAttr)
.target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7)
.punchingMove()
.partial(),
.attr(AddArenaTagAttr, ArenaTagType.PLASMA_FISTS, 1)
.punchingMove(),
new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7)
.attr(PhotonGeyserCategoryAttr)
.ignoresAbilities()

View File

@ -25,4 +25,5 @@ export enum ArenaTagType {
SAFEGUARD = "SAFEGUARD",
NO_CRIT = "NO_CRIT",
IMPRISON = "IMPRISON",
PLASMA_FISTS = "PLASMA_FISTS",
}

View File

@ -1518,6 +1518,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
this.scene.arena.applyTags(ArenaTagType.PLASMA_FISTS, moveTypeHolder);
return moveTypeHolder.value as Type;
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "Lehmsuhler hört auf zu wirken!",
"waterSportOnAdd": "Die Stärke aller Feuer-Attacken wurde reduziert!",
"waterSportOnRemove": "Nassmacher hört auf zu wirken!",
"plasmaFistsOnAdd": "Ein elektrisch geladener Niederschlag regnet auf das Kampffeld herab!",
"spikesOnAdd": "Die {{opponentDesc}} sind von Stacheln umgeben!",
"spikesActivateTrap": "Die {{pokemonNameWithAffix}} wurde durch Stachler verletzt!!",
"toxicSpikesOnAdd": "Die {{opponentDesc}} sind überall von giftigen Stacheln umgeben",
@ -54,4 +55,4 @@
"safeguardOnRemove": "Der mystische Schleier, der das ganze Feld umgab, hat sich gelüftet!",
"safeguardOnRemovePlayer": "Der mystische Schleier, der dein Team umgab, hat sich gelüftet!",
"safeguardOnRemoveEnemy": "Der mystische Schleier, der das gegnerische Team umgab, hat sich gelüftet!"
}
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
"waterSportOnAdd": "Fire's power was weakened!",
"waterSportOnRemove": "The effects of Water Sport\nhave faded.",
"plasmaFistsOnAdd": "A deluge of ions showers the battlefield!",
"spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",
"spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!",
"toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!",

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.",
"waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!",
"waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.",
"plasmaFistsOnAdd": "¡Una lluvia de electrones cae sobre\nel terreno de combate!",
"spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
"spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!",
"toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
@ -54,4 +55,4 @@
"safeguardOnRemove": "¡Velo Sagrado dejó de hacer efecto!",
"safeguardOnRemovePlayer": "El efecto de Velo Sagrado en tu equipo se ha disipado.",
"safeguardOnRemoveEnemy": "El efecto de Velo Sagrado en el equipo enemigo se ha disipado."
}
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "Leffet de Lance-Boue se dissipe !",
"waterSportOnAdd": "La puissance des capacités\nde type Feu diminue !",
"waterSportOnRemove": "Leffet de Tourniquet se dissipe !",
"plasmaFistsOnAdd": "Un déluge de plasma sabat sur le terrain !",
"spikesOnAdd": "Des {{moveName}} séparpillent autour de {{opponentDesc}} !",
"spikesActivateTrap": "{{pokemonNameWithAffix}} est blessé\npar les picots !",
"toxicSpikesOnAdd": "Des {{moveName}} séparpillent autour de {{opponentDesc}} !",

View File

@ -1,8 +1,9 @@
{
"plasmaFistsOnAdd": "Una pioggia di elettroni si rovescia sui Pokémon!",
"safeguardOnAdd": "Un velo mistico ricopre il campo!",
"safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!",
"safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!",
"safeguardOnRemove": "Il campo non è più protetto da Salvaguardia!",
"safeguardOnRemovePlayer": "La tua squadra non è più protetta da Salvaguardia!",
"safeguardOnRemoveEnemy": "La squadra avversaria non è più protetta da Salvaguardia!"
}
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "どろあそびの 効果が なくなった!",
"waterSportOnAdd": "炎の威力が 弱まった!",
"waterSportOnRemove": "みずあそびの 効果が なくなった!",
"plasmaFistsOnAdd": "電子のシャワーが 降りそそいだ!",
"spikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",
"spikesActivateTrap": "{{pokemonNameWithAffix}}は\nまきびしの ダメージを 受けた",
"toxicSpikesOnAdd": "{{opponentDesc}}の 足下に\n{{moveName}}が 散らばった!",

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "흙놀이의 효과가\n없어졌다!",
"waterSportOnAdd": "불꽃의 위력이 약해졌다!",
"waterSportOnRemove": "물놀이의 효과가\n없어졌다!",
"plasmaFistsOnAdd": "전기 입자가 쏟아졌다!",
"spikesOnAdd": "{{opponentDesc}}의 발밑에\n압정이 뿌려졌다!",
"spikesActivateTrap": "{{pokemonNameWithAffix}}[[는]]\n압정뿌리기의 데미지를 입었다!",
"toxicSpikesOnAdd": "{{opponentDesc}}의 발밑에\n독압정이 뿌려졌다!",
@ -54,4 +55,4 @@
"safeguardOnRemove": "필드를 감싸던 신비의 베일이 없어졌다!",
"safeguardOnRemovePlayer": "우리 편을 감싸던 신비의 베일이 없어졌다!",
"safeguardOnRemoveEnemy": "상대 편을 감싸던 신비의 베일이 없어졌다!"
}
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.",
"waterSportOnAdd": "O poder de movimentos de fogo foi enfraquecido!",
"waterSportOnRemove": "Os efeitos de Water Sport\nsumiram.",
"plasmaFistsOnAdd": "Um dilúvio de íons chove sobre o campo de batalha!",
"spikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",
"spikesActivateTrap": "{{pokemonNameWithAffix}} foi ferido\npelos espinhos!",
"toxicSpikesOnAdd": "{{moveName}} foram espalhados\nno chão ao redor de {{opponentDesc}}!",

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "玩泥巴的效果消失了!",
"waterSportOnAdd": "火焰的威力减弱了!",
"waterSportOnRemove": "玩水的效果消失了!",
"plasmaFistsOnAdd": "等离子雨倾盆而下!",
"spikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}",
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的伤害",
"toxicSpikesOnAdd": "{{opponentDesc}}脚下\n散落着{{moveName}}",
@ -54,4 +55,4 @@
"safeguardOnRemove": "包围整个场地的\n神秘之幕消失了",
"safeguardOnRemovePlayer": "包围我方的\n神秘之幕消失了",
"safeguardOnRemoveEnemy": "包围对手的\n神秘之幕消失了"
}
}

View File

@ -28,6 +28,7 @@
"mudSportOnRemove": "玩泥巴的效果消失了!",
"waterSportOnAdd": "火焰的威力減弱了!",
"waterSportOnRemove": "玩水的效果消失了!",
"plasmaFistsOnAdd": "等離子雨傾盆而下!",
"spikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}",
"spikesActivateTrap": "{{pokemonNameWithAffix}}\n受到了撒菱的傷害",
"toxicSpikesOnAdd": "{{opponentDesc}}腳下\n散落著{{moveName}}",

View File

@ -0,0 +1,98 @@
import { BattlerIndex } from "#app/battle";
import { Type } from "#app/data/type";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
describe("Moves - Plasma Fists", () => {
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.PLASMA_FISTS, Moves.TACKLE])
.battleType("double")
.startingLevel(100)
.enemySpecies(Species.DUSCLOPS)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.TACKLE)
.enemyLevel(100);
});
it("should convert all subsequent Normal-type attacks to Electric-type", async () => {
await game.classicMode.startBattle([Species.DUSCLOPS, Species.BLASTOISE]);
const field = game.scene.getField(true);
field.forEach(p => vi.spyOn(p, "getMoveType"));
game.move.select(Moves.PLASMA_FISTS, 0, BattlerIndex.ENEMY);
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY_2);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to("BerryPhase", false);
field.forEach(p => {
expect(p.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC);
expect(p.hp).toBeLessThan(p.getMaxHp());
});
});
it("should not affect Normal-type attacks boosted by Pixilate", async () => {
game.override
.battleType("single")
.enemyAbility(Abilities.PIXILATE);
await game.classicMode.startBattle([Species.ONIX]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
vi.spyOn(enemyPokemon, "getMoveType");
game.move.select(Moves.PLASMA_FISTS);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.FAIRY);
expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp());
});
it("should affect moves that become Normal type due to Normalize", async () => {
game.override
.battleType("single")
.enemyAbility(Abilities.NORMALIZE)
.enemyMoveset(Moves.WATER_GUN);
await game.classicMode.startBattle([Species.DUSCLOPS]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
vi.spyOn(enemyPokemon, "getMoveType");
game.move.select(Moves.PLASMA_FISTS);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC);
expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp());
});
});

View File

@ -303,7 +303,7 @@ export default class GameManager {
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
move: moveId,
targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target))
targets: (target !== undefined && !legalTargets.multiple && legalTargets.targets.includes(target))
? [target]
: enemy.getNextTargets(moveId)
});