Merge 41cebcc182
into 1b751dddec
commit
cef6cae14d
|
@ -1,7 +1,7 @@
|
|||
import { CommonAnim, CommonBattleAnim } from "./battle-anims";
|
||||
import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases";
|
||||
import { getPokemonMessage, getPokemonPrefix } from "../messages";
|
||||
import Pokemon, { MoveResult, HitResult } from "../field/pokemon";
|
||||
import Pokemon, { MoveResult, HitResult, TurnMove } from "../field/pokemon";
|
||||
import { Stat, getStatName } from "./pokemon-stat";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import * as Utils from "../utils";
|
||||
|
@ -488,6 +488,68 @@ export class EncoreTag extends BattlerTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class TormentedTag extends BattlerTag {
|
||||
public moveId: Moves;
|
||||
constructor(sourceId: integer) {
|
||||
super(BattlerTagType.TORMENTED, BattlerTagLapseType.TURN_END, 1, Moves.TORMENT, sourceId);
|
||||
}
|
||||
/**
|
||||
* When given a battler tag or json representing one, load the data for it.
|
||||
* @param {BattlerTag | any} source A battler tag
|
||||
*/
|
||||
loadTag(source: BattlerTag | any): void {
|
||||
super.loadTag(source);
|
||||
this.moveId = source.moveId as Moves;
|
||||
}
|
||||
|
||||
canAdd(pokemon: Pokemon): boolean {
|
||||
if (pokemon.isMax())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' was subjected\nto torment!'));
|
||||
}
|
||||
|
||||
onOverlap(pokemon: Pokemon): void {
|
||||
super.onOverlap(pokemon);
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
super.onRemove(pokemon);
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, 'is no longer\ntormented!'));
|
||||
}
|
||||
|
||||
//At the end of each turn, set this pokemon's unselectableMove to the last move it executed. If this move is struggle, instead unset unselectableMove. If no valid move is found, do nothing.
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
if (ret) {
|
||||
const moveQueue = pokemon.getLastXMoves();
|
||||
let turnMove: TurnMove;
|
||||
while (moveQueue.length) {
|
||||
turnMove = moveQueue.shift();
|
||||
if (turnMove.virtual && turnMove.move !== Moves.STRUGGLE)
|
||||
continue;
|
||||
if (turnMove.move === Moves.STRUGGLE) {
|
||||
pokemon.summonData.unselectableMove = Moves.NONE;
|
||||
return ret;
|
||||
}
|
||||
const moveIndex = pokemon.getMoveset().findIndex(m => m.moveId === turnMove.move);
|
||||
if (moveIndex === -1)
|
||||
return ret;
|
||||
else {
|
||||
pokemon.summonData.unselectableMove = turnMove.move;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class HelpingHandTag extends BattlerTag {
|
||||
constructor(sourceId: integer) {
|
||||
super(BattlerTagType.HELPING_HAND, BattlerTagLapseType.TURN_END, 1, Moves.HELPING_HAND, sourceId);
|
||||
|
@ -1297,7 +1359,9 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
|
|||
case BattlerTagType.CHARGING:
|
||||
return new ChargingTag(sourceMove, sourceId);
|
||||
case BattlerTagType.ENCORE:
|
||||
return new EncoreTag(sourceId);
|
||||
return new EncoreTag(sourceId);
|
||||
case BattlerTagType.TORMENTED:
|
||||
return new TormentedTag(sourceId);
|
||||
case BattlerTagType.HELPING_HAND:
|
||||
return new HelpingHandTag(sourceId);
|
||||
case BattlerTagType.INGRAIN:
|
||||
|
|
|
@ -11,6 +11,7 @@ export enum BattlerTagType {
|
|||
FRENZY = "FRENZY",
|
||||
CHARGING = "CHARGING",
|
||||
ENCORE = "ENCORE",
|
||||
TORMENTED = "TORMENTED",
|
||||
HELPING_HAND = "HELPING_HAND",
|
||||
INGRAIN = "INGRAIN",
|
||||
AQUA_RING = "AQUA_RING",
|
||||
|
|
|
@ -3078,7 +3078,8 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
|||
case BattlerTagType.INFESTATION:
|
||||
return -3;
|
||||
case BattlerTagType.ENCORE:
|
||||
return -2;
|
||||
return -2;
|
||||
case BattlerTagType.TORMENTED:
|
||||
case BattlerTagType.INGRAIN:
|
||||
case BattlerTagType.IGNORE_ACCURACY:
|
||||
case BattlerTagType.AQUA_RING:
|
||||
|
@ -5092,8 +5093,9 @@ export function initMoves() {
|
|||
new StatusMove(Moves.HAIL, Type.ICE, -1, 10, -1, 0, 3)
|
||||
.attr(WeatherChangeAttr, WeatherType.HAIL)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3)
|
||||
.unimplemented(),
|
||||
new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.TORMENTED, false, true)
|
||||
.condition((user, target, move) => !target.getTag(BattlerTagType.TORMENTED) && !target.isMax()),
|
||||
new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3)
|
||||
.attr(StatChangeAttr, BattleStat.SPATK, 1)
|
||||
.attr(ConfuseAttr),
|
||||
|
|
|
@ -1321,11 +1321,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger);
|
||||
}
|
||||
|
||||
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||
trySelectMove(moveIndex: integer, ignorePp?: boolean, skipSelection?: boolean): boolean {
|
||||
const move = this.getMoveset().length > moveIndex
|
||||
? this.getMoveset()[moveIndex]
|
||||
: null;
|
||||
return move?.isUsable(this, ignorePp);
|
||||
return move?.isUsable(this, ignorePp) && (move?.isSelectable(this, ignorePp) || skipSelection);
|
||||
}
|
||||
|
||||
showInfo(): void {
|
||||
|
@ -2970,16 +2970,19 @@ export class EnemyPokemon extends Pokemon {
|
|||
}
|
||||
}
|
||||
|
||||
const movePool = this.getMoveset().filter(m => m.isUsable(this));
|
||||
const movePool = this.getMoveset().filter(m => m.isUsable(this)).filter(m => m.isSelectable(this));
|
||||
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) {
|
||||
if (this.summonData.disabledMove === encoreTag.moveId || this.summonData.unselectableMove === encoreTag.moveId) {
|
||||
return { move: Moves.STRUGGLE, targets: this.getNextTargets(Moves.STRUGGLE) };
|
||||
}
|
||||
const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId);
|
||||
if (encoreMove)
|
||||
return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) };
|
||||
}
|
||||
}
|
||||
if (movePool.length === 1)
|
||||
return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) };
|
||||
switch (this.aiType) {
|
||||
case AiType.RANDOM:
|
||||
const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)].moveId;
|
||||
|
@ -3316,6 +3319,10 @@ export class PokemonSummonData {
|
|||
public moveQueue: QueuedMove[] = [];
|
||||
public disabledMove: Moves = Moves.NONE;
|
||||
public disabledTurns: integer = 0;
|
||||
/*
|
||||
* Moves that can not be selected in the UI, but don't fail when used for other reasons. Currently just used by Torment.
|
||||
*/
|
||||
public unselectableMove: Moves = Moves.NONE;
|
||||
public tags: BattlerTag[] = [];
|
||||
public abilitySuppressed: boolean = false;
|
||||
|
||||
|
@ -3400,6 +3407,12 @@ export class PokemonMove {
|
|||
return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1) && !this.getMove().name.endsWith(' (N)');
|
||||
}
|
||||
|
||||
isSelectable(pokemon: Pokemon, ignorePp?: boolean): boolean {
|
||||
if ((this.moveId && pokemon.summonData?.disabledMove === this.moveId) || (this.moveId && pokemon.summonData?.unselectableMove === this.moveId))
|
||||
return false;
|
||||
return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1) && !this.getMove().name.endsWith(' (N)');
|
||||
}
|
||||
|
||||
getMove(): Move {
|
||||
return allMoves[this.moveId];
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ export const battle: SimpleTranslationEntries = {
|
|||
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
|
||||
"moveNoPP": "There's no PP left for\nthis move!",
|
||||
"moveDisabled": "{{moveName}} is disabled!",
|
||||
"moveTormented": "{{pokemonName}} can't use the same move\n twice in a row due to the torment!",
|
||||
"noPokeballForce": "An unseen force\nprevents using Poké Balls.",
|
||||
"noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!",
|
||||
"noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!",
|
||||
|
|
|
@ -1681,11 +1681,12 @@ export class CommandPhase extends FieldPhase {
|
|||
if (moveQueue.length) {
|
||||
const queuedMove = moveQueue[0];
|
||||
if (!queuedMove.move)
|
||||
this.handleCommand(Command.FIGHT, -1, false);
|
||||
this.handleCommand(Command.FIGHT, -1, false, false);
|
||||
else {
|
||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
|
||||
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, queuedMove.ignorePP)) {
|
||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
||||
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, queuedMove.ignorePP)) {
|
||||
//if the player already has a move queued (e.g. due to outrage), have arg1 be true in handleCommand
|
||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, true, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
||||
} else
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}
|
||||
|
@ -1697,14 +1698,21 @@ export class CommandPhase extends FieldPhase {
|
|||
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex];
|
||||
const enemyField = this.scene.getEnemyField();
|
||||
let success: boolean;
|
||||
|
||||
|
||||
switch (command) {
|
||||
case Command.FIGHT:
|
||||
let useStruggle = false;
|
||||
let useStruggleA = false;
|
||||
let useStruggleB = false;
|
||||
let useStruggleC = false;
|
||||
const encoreTag = playerPokemon.getTag(EncoreTag) as EncoreTag;
|
||||
//Lengthy check for whether or not the player's choice is legal, and if they should struggle.
|
||||
if (cursor === -1 ||
|
||||
playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
|
||||
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) {
|
||||
const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor].moveId : Moves.NONE : Moves.STRUGGLE;
|
||||
playerPokemon.trySelectMove(cursor, args[0] as boolean, args[1] as boolean) ||
|
||||
(useStruggleA = cursor > -1 && (!playerPokemon.getMoveset().filter(m => m.isSelectable(playerPokemon)).length) && !args[1]) ||
|
||||
(useStruggleB = cursor > -1 && (!playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) ||
|
||||
(useStruggleC = cursor > -1 && encoreTag && encoreTag.moveId != Moves.NONE && (encoreTag.moveId === playerPokemon.summonData.unselectableMove || encoreTag.moveId === playerPokemon.summonData.disabledMove))) {
|
||||
const moveId = !(useStruggleA || useStruggleB || useStruggleC) ? cursor > -1 ? playerPokemon.getMoveset()[cursor].moveId : Moves.NONE : Moves.STRUGGLE;
|
||||
args.splice(1, 1);
|
||||
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
||||
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2];
|
||||
if (!moveId)
|
||||
|
@ -1723,13 +1731,14 @@ export class CommandPhase extends FieldPhase {
|
|||
const move = playerPokemon.getMoveset()[cursor];
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
|
||||
// Decides between a Disabled, Not Implemented, or No PP translation message
|
||||
// Decides between a Tormented, Disabled, Not Implemented, or No PP translation message
|
||||
const errorMessage =
|
||||
playerPokemon.summonData.disabledMove === move.moveId ? 'battle:moveDisabled' :
|
||||
playerPokemon.summonData.unselectableMove === move.moveId ? 'battle:moveTormented' : playerPokemon.summonData.disabledMove === move.moveId ? 'battle:moveDisabled' :
|
||||
move.getName().endsWith(' (N)') ? 'battle:moveNotImplemented' : 'battle:moveNoPP';
|
||||
const moveName = move.getName().replace(' (N)', ''); // Trims off the indicator
|
||||
const pokemonName = playerPokemon.name;
|
||||
|
||||
this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => {
|
||||
this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName, pokemonName: pokemonName }), null, () => {
|
||||
this.scene.ui.clearText();
|
||||
this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex);
|
||||
}, null, true);
|
||||
|
@ -1854,10 +1863,17 @@ export class CommandPhase extends FieldPhase {
|
|||
|
||||
const moveIndex = pokemon.getMoveset().findIndex(m => m.moveId === encoreTag.moveId);
|
||||
|
||||
if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex].isUsable(pokemon))
|
||||
return false;
|
||||
if (moveIndex === -1 || (!(pokemon.getMoveset()[moveIndex].ppUsed < pokemon.getMoveset()[moveIndex].getMovePp() ||
|
||||
pokemon.getMoveset()[moveIndex].getMove().pp === -1) && !pokemon.getMoveset()[moveIndex].getMove().name.endsWith(' (N)'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.handleCommand(Command.FIGHT, moveIndex, false);
|
||||
|
||||
if (!pokemon.getMoveset()[moveIndex].isUsable(pokemon) || !pokemon.getMoveset()[moveIndex].isSelectable(pokemon)) {
|
||||
this.handleCommand(Command.FIGHT, 5, true, false);
|
||||
return true;
|
||||
}
|
||||
this.handleCommand(Command.FIGHT, moveIndex, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ export default class PokemonData {
|
|||
this.summonData.moveQueue = source.summonData.moveQueue;
|
||||
this.summonData.disabledMove = source.summonData.disabledMove;
|
||||
this.summonData.disabledTurns = source.summonData.disabledTurns;
|
||||
this.summonData.unselectableMove = source.summonData.unselectableMove;
|
||||
this.summonData.abilitySuppressed = source.summonData.abilitySuppressed;
|
||||
|
||||
this.summonData.ability = source.summonData.ability;
|
||||
|
|
|
@ -100,7 +100,7 @@ export default class FightUiHandler extends UiHandler {
|
|||
|
||||
if (button === Button.CANCEL || button === Button.ACTION) {
|
||||
if (button === Button.ACTION) {
|
||||
if ((this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, cursor, false))
|
||||
if ((this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, cursor, false, false))
|
||||
success = true;
|
||||
else
|
||||
ui.playError();
|
||||
|
|
Loading…
Reference in New Issue