From 85866169ac0ebae1d98f22b379852a4a0f98eb80 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Thu, 16 Nov 2023 00:58:57 -0500 Subject: [PATCH] Implement encore move --- src/battle-phases.ts | 23 ++++++++++-- src/data/battler-tag.ts | 69 ++++++++++++++++++++++++++++++++---- src/data/move.ts | 39 ++++++++++---------- src/pokemon.ts | 8 ++++- src/ui/command-ui-handler.ts | 2 ++ 5 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/battle-phases.ts b/src/battle-phases.ts index eea764336..d9ad00024 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -18,7 +18,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr import { Biome, biomeLinks } from "./data/biome"; import { FusePokemonModifierType, ModifierPoolType, ModifierTier, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, RememberMoveModifierType, TmModifierType, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag"; +import { BattlerTagLapseType, BattlerTagType, EncoreTag, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag"; import { getPokemonMessage } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; @@ -1002,8 +1002,7 @@ export class CommandPhase extends FieldPhase { let useStruggle = false; if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) { const moveId = !useStruggle ? playerPokemon.getMoveset()[cursor].moveId : Moves.STRUGGLE; - const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, - move: cursor > -1 ? { move: moveId, targets: [] } : null, args: args }; + const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: cursor > -1 ? { move: moveId, targets: [] } : null, args: args }; const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, cursor > -1 ? moveId : Moves.NONE) : args[2]; console.log(moveTargets, playerPokemon.name); if (moveTargets.targets.length <= 1 || moveTargets.multiple) @@ -1116,6 +1115,24 @@ export class CommandPhase extends FieldPhase { } } + checkFightOverride(): boolean { + const pokemon = this.getPokemon(); + + const encoreTag = pokemon.getTag(EncoreTag) as EncoreTag; + + if (!encoreTag) + return false; + + const moveIndex = pokemon.getMoveset().findIndex(m => m.moveId === encoreTag.moveId); + + if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex].isUsable(pokemon)) + return false; + + this.handleCommand(Command.FIGHT, moveIndex, false); + + return true; + } + getFieldIndex(): integer { return this.fieldIndex; } diff --git a/src/data/battler-tag.ts b/src/data/battler-tag.ts index 3e87938be..9d4fa3445 100644 --- a/src/data/battler-tag.ts +++ b/src/data/battler-tag.ts @@ -5,7 +5,7 @@ import Pokemon, { MoveResult } from "../pokemon"; import { Stat } from "./pokemon-stat"; import { StatusEffect } from "./status-effect"; import * as Utils from "../utils"; -import { Moves, allMoves } from "./move"; +import { ChargeAttr, Moves, allMoves } from "./move"; import { Type } from "./type"; import { Abilities } from "./ability"; @@ -289,7 +289,7 @@ export class SeedTag extends BattlerTag { const damage = Math.max(Math.floor(pokemon.getMaxHp() / 8), 1); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); pokemon.damage(damage); - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by LEECH SEED!'), false, true)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true)); } return ret; @@ -308,20 +308,20 @@ export class NightmareTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' began\nhaving a NIGHTMARE!')); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' began\nhaving a Nightmare!')); } onOverlap(pokemon: Pokemon): void { super.onOverlap(pokemon); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nalready locked in a NIGHTMARE!')); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nalready locked in a Nightmare!')); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a NIGHTMARE!')); + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a Nightmare!')); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type const damage = Math.ceil(pokemon.getMaxHp() / 4); @@ -349,6 +349,59 @@ export class FrenzyTag extends BattlerTag { } } +export class EncoreTag extends BattlerTag { + public moveId: Moves; + + constructor(sourceMove: Moves, sourceId: integer) { + super(BattlerTagType.ENCORE, BattlerTagLapseType.AFTER_MOVE, 3, sourceMove, sourceId); + } + + canAdd(pokemon: Pokemon): boolean { + const lastMoves = pokemon.getLastXMoves(1); + if (!lastMoves.length) + return false; + + const repeatableMove = lastMoves[0]; + + if (!repeatableMove.move || repeatableMove.virtual) + return false; + + switch (repeatableMove.move) { + case Moves.MIMIC: + case Moves.MIRROR_MOVE: + case Moves.TRANSFORM: + case Moves.STRUGGLE: + case Moves.SKETCH: + case Moves.SLEEP_TALK: + case Moves.ENCORE: + return false; + } + + if (allMoves[repeatableMove.move].getAttrs(ChargeAttr).length && repeatableMove.result === MoveResult.OTHER) + return false; + + this.moveId = repeatableMove.move; + + return true; + } + + onAdd(pokemon: Pokemon): void { + super.onRemove(pokemon); + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' got\nan Encore!')); + + const turnCommand = pokemon.scene.currentBattle.turnCommands[pokemon.getFieldIndex()]; + if (turnCommand) + turnCommand.move = { move: this.moveId, targets: pokemon.getLastXMoves(1)[0].targets }; + } + + onRemove(pokemon: Pokemon): void { + super.onRemove(pokemon); + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\'s Encore\nended!')); + } +} + export class IngrainTag extends TrappedTag { constructor(sourceId: integer) { super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1, Moves.INGRAIN, sourceId); @@ -465,7 +518,7 @@ export class WrapTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon, ` was WRAPPED\nby ${pokemon.scene.getPokemonById(this.sourceId).name}!`); + return getPokemonMessage(pokemon, ` was Wrapped\nby ${pokemon.scene.getPokemonById(this.sourceId).name}!`); } } @@ -497,7 +550,7 @@ export class ClampTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { - return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` CLAMPED\n${pokemon.name}!`); + return getPokemonMessage(pokemon.scene.getPokemonById(this.sourceId), ` Clamped\n${pokemon.name}!`); } } @@ -642,6 +695,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc return new NightmareTag(); case BattlerTagType.FRENZY: return new FrenzyTag(sourceMove, sourceId); + case BattlerTagType.ENCORE: + return new EncoreTag(sourceMove, sourceId); case BattlerTagType.INGRAIN: return new IngrainTag(sourceId); case BattlerTagType.AQUA_RING: diff --git a/src/data/move.ts b/src/data/move.ts index 04931aa31..6aa356d80 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,7 +1,7 @@ import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; import { BattleEndPhase, DamagePhase, MovePhase, NewBattlePhase, ObtainStatusEffectPhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../battle-phases"; import { BattleStat } from "./battle-stat"; -import { BattlerTagType } from "./battler-tag"; +import { BattlerTagType, EncoreTag } from "./battler-tag"; import { getPokemonMessage } from "../messages"; import Pokemon, { AttackMoveResult, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon"; import { StatusEffect, getStatusEffectDescriptor } from "./status-effect"; @@ -2177,16 +2177,16 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr { // TODO: Review this const targetMoveCopiableCondition = (user: Pokemon, target: Pokemon, move: Move) => { const targetMoves = target.getMoveHistory().filter(m => !m.virtual); - if (!targetMoves.length) - return false; + if (!targetMoves.length) + return false; - const copiableMove = targetMoves[0]; + const copiableMove = targetMoves[0]; - if (!copiableMove.move) - return false; + if (!copiableMove.move) + return false; - if (allMoves[copiableMove.move].getAttrs(ChargeAttr).length && copiableMove.result === MoveResult.OTHER) - return false; + if (allMoves[copiableMove.move].getAttrs(ChargeAttr).length && copiableMove.result === MoveResult.OTHER) + return false; // TODO: Add last turn of Bide @@ -2252,15 +2252,15 @@ export class SketchAttr extends MoveEffectAttr { return false; const targetMoves = target.getMoveHistory().filter(m => !m.virtual); - if (!targetMoves.length) - return false; - - const sketchableMove = targetMoves[0]; - - if (user.getMoveset().find(m => m.moveId === sketchableMove.move)) - return false; - - return true; + if (!targetMoves.length) + return false; + + const sketchableMove = targetMoves[0]; + + if (user.getMoveset().find(m => m.moveId === sketchableMove.move)) + return false; + + return true; }; } } @@ -2907,8 +2907,9 @@ export function initMoves() { new SelfStatusMove(Moves.BATON_PASS, "Baton Pass", Type.NORMAL, -1, 40, 132, "User switches out and gives stat changes to the incoming Pokémon.", -1, 0, 2) .attr(ForceSwitchOutAttr, true, true) .hidesUser(), - new StatusMove(Moves.ENCORE, "Encore (N)", Type.NORMAL, 100, 5, 122, "Forces opponent to keep using its last move for 3 turns.", -1, 0, 2) - .attr(AddBattlerTagAttr, BattlerTagType.ENCORE, false, undefined, true), + new StatusMove(Moves.ENCORE, "Encore", Type.NORMAL, 100, 5, 122, "Forces opponent to keep using its last move for 3 turns.", -1, 0, 2) + .attr(AddBattlerTagAttr, BattlerTagType.ENCORE, false, undefined, true) + .condition((user: Pokemon, target: Pokemon, move: Move) => new EncoreTag(move.id, user.id).canAdd(target)), new AttackMove(Moves.PURSUIT, "Pursuit (N)", Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, "Double power if the opponent is switching out.", -1, 0, 2), new AttackMove(Moves.RAPID_SPIN, "Rapid Spin", Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, -1, "Raises user's Speed and removes entry hazards and trap move effects.", 100, 0, 2) .attr(StatChangeAttr, BattleStat.SPD, 1, true) diff --git a/src/pokemon.ts b/src/pokemon.ts index 10efb8e47..695f4db56 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -16,7 +16,7 @@ import { tmSpecies } from './data/tms'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './data/pokemon-evolutions'; import { DamagePhase, FaintPhase, SwitchSummonPhase } from './battle-phases'; import { BattleStat } from './data/battle-stat'; -import { BattlerTag, BattlerTagLapseType, BattlerTagType, TypeBoostTag, getBattlerTag } from './data/battler-tag'; +import { BattlerTag, BattlerTagLapseType, BattlerTagType, EncoreTag, TypeBoostTag, getBattlerTag } from './data/battler-tag'; import { Species } from './data/species'; import { WeatherType } from './data/weather'; import { TempBattleStat } from './data/temp-battle-stat'; @@ -1569,6 +1569,12 @@ export class EnemyPokemon extends Pokemon { if (movePool.length) { if (movePool.length === 1) return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) }; + const encoreTag = this.getTag(EncoreTag) as EncoreTag; + if (encoreTag) { + const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId); + if (encoreMove) + return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) }; + } switch (this.aiType) { case AiType.RANDOM: const moveId = movePool[Utils.randInt(movePool.length)].moveId; diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 5cdc6aec6..b083ba02f 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -63,6 +63,8 @@ export default class CommandUiHandler extends UiHandler { if (button === Button.ACTION) { switch (cursor) { case 0: + if ((this.scene.getCurrentPhase() as CommandPhase).checkFightOverride()) + return true; ui.setMode(Mode.FIGHT, (this.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); success = true; break;