mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-25 00:06:18 +00:00
[Move] Implement Power Trick (#2658)
* add `PowerTrickTag` * modify getStat() with PowerTrickTag * implement `PowerTrickAttr` * add unit test * enhance docs and tag apply logic --------- Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Enoch <enoch.jwsong@gmail.com> Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com> Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
676322e800
commit
e7a4d4055f
@ -2724,6 +2724,44 @@ export class TelekinesisTag extends BattlerTag {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag that swaps the user's base ATK stat with its base DEF stat.
|
||||
* @extends BattlerTag
|
||||
*/
|
||||
export class PowerTrickTag extends BattlerTag {
|
||||
constructor(sourceMove: Moves, sourceId: number) {
|
||||
super(BattlerTagType.POWER_TRICK, BattlerTagLapseType.CUSTOM, 0, sourceMove, sourceId, true);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
this.swapStat(pokemon);
|
||||
pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
this.swapStat(pokemon);
|
||||
pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Power Trick tag and reverts any stat changes if the tag is already applied.
|
||||
* @param {Pokemon} pokemon The {@linkcode Pokemon} that already has the Power Trick tag.
|
||||
*/
|
||||
onOverlap(pokemon: Pokemon): void {
|
||||
pokemon.removeTag(this.tagType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the user's base ATK stat with its base DEF stat.
|
||||
* @param {Pokemon} pokemon The {@linkcode Pokemon} whose stats will be swapped.
|
||||
*/
|
||||
swapStat(pokemon: Pokemon): void {
|
||||
const temp = pokemon.getStat(Stat.ATK, false);
|
||||
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.DEF, false), false);
|
||||
pokemon.setStat(Stat.DEF, temp, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
|
||||
* @param sourceId - The ID of the pokemon adding the tag
|
||||
@ -2899,6 +2937,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
||||
return new SyrupBombTag(sourceId);
|
||||
case BattlerTagType.TELEKINESIS:
|
||||
return new TelekinesisTag(sourceMove);
|
||||
case BattlerTagType.POWER_TRICK:
|
||||
return new PowerTrickTag(sourceMove, sourceId);
|
||||
case BattlerTagType.NONE:
|
||||
default:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
||||
|
@ -6430,6 +6430,9 @@ export class TransformAttr extends MoveEffectAttr {
|
||||
user.summonData.gender = target.getGender();
|
||||
user.summonData.fusionGender = target.getFusionGender();
|
||||
|
||||
// Power Trick's effect will not preserved after using Transform
|
||||
user.removeTag(BattlerTagType.POWER_TRICK);
|
||||
|
||||
// Copy all stats (except HP)
|
||||
for (const s of EFFECTIVE_STATS) {
|
||||
user.setStat(s, target.getStat(s, false), false);
|
||||
@ -8153,7 +8156,7 @@ export function initMoves() {
|
||||
.attr(OpponentHighHpPowerAttr, 120)
|
||||
.makesContact(),
|
||||
new SelfStatusMove(Moves.POWER_TRICK, Type.PSYCHIC, -1, 10, -1, 0, 4)
|
||||
.unimplemented(),
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.POWER_TRICK, true),
|
||||
new StatusMove(Moves.GASTRO_ACID, Type.POISON, 100, 10, -1, 0, 4)
|
||||
.attr(SuppressAbilitiesAttr),
|
||||
new StatusMove(Moves.LUCKY_CHANT, Type.NORMAL, -1, 30, -1, 0, 4)
|
||||
|
@ -80,6 +80,7 @@ export enum BattlerTagType {
|
||||
DOUBLE_SHOCKED = "DOUBLE_SHOCKED",
|
||||
AUTOTOMIZED = "AUTOTOMIZED",
|
||||
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON",
|
||||
POWER_TRICK = "POWER_TRICK",
|
||||
HEAL_BLOCK = "HEAL_BLOCK",
|
||||
TORMENT = "TORMENT",
|
||||
TAUNT = "TAUNT",
|
||||
|
@ -19,7 +19,7 @@ import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||
import { Status, StatusEffect, getRandomStatus } from "#app/data/status-effect";
|
||||
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms";
|
||||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag } from "../data/battler-tags";
|
||||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
|
||||
import { WeatherType } from "#app/data/weather";
|
||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs } from "#app/data/ability";
|
||||
@ -3048,6 +3048,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag instanceof PowerTrickTag) {
|
||||
tag.swapStat(this);
|
||||
}
|
||||
|
||||
this.summonData.tags.push(tag);
|
||||
}
|
||||
|
||||
|
113
src/test/moves/power_trick.test.ts
Normal file
113
src/test/moves/power_trick.test.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Species } from "#enums/species";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
|
||||
describe("Moves - Power Trick", () => {
|
||||
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")
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.enemySpecies(Species.MEW)
|
||||
.enemyLevel(200)
|
||||
.moveset([ Moves.POWER_TRICK ])
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("swaps the user's ATK and DEF stats", async () => {
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const baseATK = player.getStat(Stat.ATK, false);
|
||||
const baseDEF = player.getStat(Stat.DEF, false);
|
||||
|
||||
game.move.select(Moves.POWER_TRICK);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(player.getStat(Stat.ATK, false)).toBe(baseDEF);
|
||||
expect(player.getStat(Stat.DEF, false)).toBe(baseATK);
|
||||
expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeDefined();
|
||||
});
|
||||
|
||||
it("resets initial ATK and DEF stat swap when used consecutively", async () => {
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const baseATK = player.getStat(Stat.ATK, false);
|
||||
const baseDEF = player.getStat(Stat.DEF, false);
|
||||
|
||||
game.move.select(Moves.POWER_TRICK);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
game.move.select(Moves.POWER_TRICK);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(player.getStat(Stat.ATK, false)).toBe(baseATK);
|
||||
expect(player.getStat(Stat.DEF, false)).toBe(baseDEF);
|
||||
expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should pass effect when using BATON_PASS", async () => {
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]);
|
||||
await game.override.moveset([ Moves.POWER_TRICK, Moves.BATON_PASS ]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.addTag(BattlerTagType.POWER_TRICK);
|
||||
|
||||
game.move.select(Moves.BATON_PASS);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
const switchedPlayer = game.scene.getPlayerPokemon()!;
|
||||
const baseATK = switchedPlayer.getStat(Stat.ATK);
|
||||
const baseDEF = switchedPlayer.getStat(Stat.DEF);
|
||||
|
||||
expect(switchedPlayer.getStat(Stat.ATK, false)).toBe(baseDEF);
|
||||
expect(switchedPlayer.getStat(Stat.DEF, false)).toBe(baseATK);
|
||||
expect(switchedPlayer.getTag(BattlerTagType.POWER_TRICK)).toBeDefined();
|
||||
});
|
||||
|
||||
it("should remove effect after using Transform", async () => {
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]);
|
||||
await game.override.moveset([ Moves.POWER_TRICK, Moves.TRANSFORM ]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.addTag(BattlerTagType.POWER_TRICK);
|
||||
|
||||
game.move.select(Moves.TRANSFORM);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
const baseATK = enemy.getStat(Stat.ATK);
|
||||
const baseDEF = enemy.getStat(Stat.DEF);
|
||||
|
||||
expect(player.getStat(Stat.ATK, false)).toBe(baseATK);
|
||||
expect(player.getStat(Stat.DEF, false)).toBe(baseDEF);
|
||||
expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeUndefined();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user