Merge branch 'beta' into mystery-encounters-translations
This commit is contained in:
commit
6726b1ce69
|
@ -1,7 +1,7 @@
|
|||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
title: "[Bug] "
|
||||
labels: ["Bug"]
|
||||
labels: ["Bug", "Triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
@ -19,21 +19,12 @@ body:
|
|||
value: |
|
||||
---
|
||||
- type: textarea
|
||||
id: session-file
|
||||
id: repro
|
||||
attributes:
|
||||
label: Session export file
|
||||
description: Open Menu → ManageData → Export Session → Select slot. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)).
|
||||
placeholder: Focus me and then drop your file here (or use the upload button at the bottom)
|
||||
label: Reproduction
|
||||
description: Describe the steps to reproduce this bug. If applicable attach user/session data at the bottom
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: data-file
|
||||
attributes:
|
||||
label: User data export file
|
||||
description: Open Menu → ManageData → Export Data. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)).
|
||||
placeholder: Focus me and then drop your file here (or use the upload button at the bottom)
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -60,48 +51,20 @@ body:
|
|||
attributes:
|
||||
value: |
|
||||
---
|
||||
- type: dropdown
|
||||
id: os
|
||||
- type: textarea
|
||||
id: session-file
|
||||
attributes:
|
||||
label: What OS did you observe the bug on?
|
||||
multiple: true
|
||||
options:
|
||||
- PC/Windows
|
||||
- Mac/OSX
|
||||
- Linux
|
||||
- iOS
|
||||
- Android
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os-other
|
||||
attributes:
|
||||
label: If other please specify
|
||||
label: Session export file
|
||||
description: Open Menu → ManageData → Export Session → Select slot. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)).
|
||||
placeholder: Focus me and then drop your file here (or use the upload button at the bottom)
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
- type: textarea
|
||||
id: data-file
|
||||
attributes:
|
||||
value: |
|
||||
---
|
||||
- type: dropdown
|
||||
id: browser
|
||||
attributes:
|
||||
label: Which browser do you use?
|
||||
multiple: true
|
||||
options:
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Safari
|
||||
- Edge
|
||||
- Opera
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser-other
|
||||
attributes:
|
||||
label: If other please specify
|
||||
label: User data export file
|
||||
description: Open Menu → ManageData → Export Data. The file should now be in your `/Downloads` directory. Change the file extension type from `.prsv` to `.txt` (How to [Windows](https://www.guidingtech.com/how-to-change-file-type-on-windows/) | [Mac](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) | [iOS](https://www.guidingtech.com/change-file-type-extension-on-iphone/)).
|
||||
placeholder: Focus me and then drop your file here (or use the upload button at the bottom)
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "[Feature] "
|
||||
labels: ["Enhancement"]
|
||||
labels: ["Enhancement", "Triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import BattleScene from "./battle-scene";
|
||||
import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./field/pokemon";
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import * as Utils from "./utils";
|
||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
||||
|
@ -7,6 +6,7 @@ import { GameMode } from "./game-mode";
|
|||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import { PokeballType } from "./data/pokeball";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { Moves } from "#enums/moves";
|
||||
|
@ -38,6 +38,11 @@ export interface TurnCommand {
|
|||
args?: any[];
|
||||
}
|
||||
|
||||
export interface FaintLogEntry {
|
||||
pokemon: Pokemon,
|
||||
turn: number
|
||||
}
|
||||
|
||||
interface TurnCommands {
|
||||
[key: number]: TurnCommand | null
|
||||
}
|
||||
|
@ -69,6 +74,8 @@ export default class Battle {
|
|||
public playerFaints: number = 0;
|
||||
/** The number of times a Pokemon on the enemy's side has fainted this battle */
|
||||
public enemyFaints: number = 0;
|
||||
public playerFaintsHistory: FaintLogEntry[] = [];
|
||||
public enemyFaintsHistory: FaintLogEntry[] = [];
|
||||
|
||||
private rngCounter: number = 0;
|
||||
|
||||
|
|
|
@ -1298,6 +1298,13 @@ export class ProtectedTag extends BattlerTag {
|
|||
}
|
||||
}
|
||||
|
||||
/** Base class for `BattlerTag`s that block damaging moves but not status moves */
|
||||
export class DamageProtectedTag extends ProtectedTag {}
|
||||
|
||||
/**
|
||||
* `BattlerTag` class for moves that block damaging moves damage the enemy if the enemy's move makes contact
|
||||
* Used by {@linkcode Moves.SPIKY_SHIELD}
|
||||
*/
|
||||
export class ContactDamageProtectedTag extends ProtectedTag {
|
||||
private damageRatio: number;
|
||||
|
||||
|
@ -1333,7 +1340,11 @@ export class ContactDamageProtectedTag extends ProtectedTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class ContactStatStageChangeProtectedTag extends ProtectedTag {
|
||||
/**
|
||||
* `BattlerTag` class for moves that block damaging moves and lower enemy stats if the enemy's move makes contact
|
||||
* Used by {@linkcode Moves.KINGS_SHIELD}, {@linkcode Moves.OBSTRUCT}, {@linkcode Moves.SILK_TRAP}
|
||||
*/
|
||||
export class ContactStatStageChangeProtectedTag extends DamageProtectedTag {
|
||||
private stat: BattleStat;
|
||||
private levels: number;
|
||||
|
||||
|
@ -1389,7 +1400,11 @@ export class ContactPoisonProtectedTag extends ProtectedTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class ContactBurnProtectedTag extends ProtectedTag {
|
||||
/**
|
||||
* `BattlerTag` class for moves that block damaging moves and burn the enemy if the enemy's move makes contact
|
||||
* Used by {@linkcode Moves.BURNING_BULWARK}
|
||||
*/
|
||||
export class ContactBurnProtectedTag extends DamageProtectedTag {
|
||||
constructor(sourceMove: Moves) {
|
||||
super(sourceMove, BattlerTagType.BURNING_BULWARK);
|
||||
}
|
||||
|
|
124
src/data/move.ts
124
src/data/move.ts
|
@ -81,6 +81,16 @@ export enum MoveFlags {
|
|||
MAKES_CONTACT = 1 << 0,
|
||||
IGNORE_PROTECT = 1 << 1,
|
||||
IGNORE_VIRTUAL = 1 << 2,
|
||||
/**
|
||||
* Sound-based moves have the following effects:
|
||||
* - Pokemon with the {@linkcode Abilities.SOUNDPROOF Soundproof Ability} are unaffected by other Pokemon's sound-based moves.
|
||||
* - Pokemon affected by {@linkcode Moves.THROAT_CHOP Throat Chop} cannot use sound-based moves for two turns.
|
||||
* - Sound-based moves used by a Pokemon with {@linkcode Abilities.LIQUID_VOICE Liquid Voice} become Water-type moves.
|
||||
* - Sound-based moves used by a Pokemon with {@linkcode Abilities.PUNK_ROCK Punk Rock} are boosted by 30%. Pokemon with Punk Rock also take half damage from sound-based moves.
|
||||
* - All sound-based moves (except Howl) can hit Pokemon behind an active {@linkcode Moves.SUBSTITUTE Substitute}.
|
||||
*
|
||||
* cf https://bulbapedia.bulbagarden.net/wiki/Sound-based_move
|
||||
*/
|
||||
SOUND_BASED = 1 << 3,
|
||||
HIDE_USER = 1 << 4,
|
||||
HIDE_TARGET = 1 << 5,
|
||||
|
@ -93,19 +103,20 @@ export enum MoveFlags {
|
|||
* @see {@linkcode Move.recklessMove()}
|
||||
*/
|
||||
RECKLESS_MOVE = 1 << 10,
|
||||
/** Indicates a move should be affected by {@linkcode Abilities.BULLETPROOF} */
|
||||
BALLBOMB_MOVE = 1 << 11,
|
||||
/** Grass types and pokemon with {@linkcode Abilities.OVERCOAT} are immune to powder moves */
|
||||
POWDER_MOVE = 1 << 12,
|
||||
/** Indicates a move should trigger {@linkcode Abilities.DANCER} */
|
||||
DANCE_MOVE = 1 << 13,
|
||||
/** Indicates a move should trigger {@linkcode Abilities.WIND_RIDER} */
|
||||
WIND_MOVE = 1 << 14,
|
||||
/** Indicates a move should trigger {@linkcode Abilities.TRIAGE} */
|
||||
TRIAGE_MOVE = 1 << 15,
|
||||
IGNORE_ABILITIES = 1 << 16,
|
||||
/**
|
||||
* Enables all hits of a multi-hit move to be accuracy checked individually
|
||||
*/
|
||||
/** Enables all hits of a multi-hit move to be accuracy checked individually */
|
||||
CHECK_ALL_HITS = 1 << 17,
|
||||
/**
|
||||
* Indicates a move is able to be redirected to allies in a double battle if the attacker faints
|
||||
*/
|
||||
/** Indicates a move is able to be redirected to allies in a double battle if the attacker faints */
|
||||
REDIRECT_COUNTER = 1 << 18,
|
||||
}
|
||||
|
||||
|
@ -118,22 +129,22 @@ export default class Move implements Localizable {
|
|||
private _type: Type;
|
||||
private _category: MoveCategory;
|
||||
public moveTarget: MoveTarget;
|
||||
public power: integer;
|
||||
public accuracy: integer;
|
||||
public pp: integer;
|
||||
public power: number;
|
||||
public accuracy: number;
|
||||
public pp: number;
|
||||
public effect: string;
|
||||
public chance: integer;
|
||||
public priority: integer;
|
||||
public generation: integer;
|
||||
public attrs: MoveAttr[];
|
||||
private conditions: MoveCondition[];
|
||||
private flags: integer;
|
||||
private nameAppend: string;
|
||||
/** The chance of a move's secondary effects activating */
|
||||
public chance: number;
|
||||
public priority: number;
|
||||
public generation: number;
|
||||
public attrs: MoveAttr[] = [];
|
||||
private conditions: MoveCondition[] = [];
|
||||
/** The move's {@linkcode MoveFlags} */
|
||||
private flags: number = 0;
|
||||
private nameAppend: string = "";
|
||||
|
||||
constructor(id: Moves, type: Type, category: MoveCategory, defaultMoveTarget: MoveTarget, power: integer, accuracy: integer, pp: integer, chance: integer, priority: integer, generation: integer) {
|
||||
constructor(id: Moves, type: Type, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||
this.id = id;
|
||||
|
||||
this.nameAppend = "";
|
||||
this._type = type;
|
||||
this._category = category;
|
||||
this.moveTarget = defaultMoveTarget;
|
||||
|
@ -144,10 +155,6 @@ export default class Move implements Localizable {
|
|||
this.priority = priority;
|
||||
this.generation = generation;
|
||||
|
||||
this.attrs = [];
|
||||
this.conditions = [];
|
||||
|
||||
this.flags = 0;
|
||||
if (defaultMoveTarget === MoveTarget.USER) {
|
||||
this.setFlag(MoveFlags.IGNORE_PROTECT, true);
|
||||
}
|
||||
|
@ -377,7 +384,7 @@ export default class Move implements Localizable {
|
|||
* @param makesContact The value (boolean) to set the flag to
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
makesContact(makesContact: boolean = true): this { // TODO: is true the correct default?
|
||||
makesContact(makesContact: boolean = true): this {
|
||||
this.setFlag(MoveFlags.MAKES_CONTACT, makesContact);
|
||||
return this;
|
||||
}
|
||||
|
@ -388,7 +395,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.CURSE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
ignoresProtect(ignoresProtect: boolean = true): this { // TODO: is `true` the correct default?
|
||||
ignoresProtect(ignoresProtect: boolean = true): this {
|
||||
this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect);
|
||||
return this;
|
||||
}
|
||||
|
@ -399,7 +406,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.NATURE_POWER}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
ignoresVirtual(ignoresVirtual: boolean = true): this { // TODO: is `true` the correct default?
|
||||
ignoresVirtual(ignoresVirtual: boolean = true): this {
|
||||
this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual);
|
||||
return this;
|
||||
}
|
||||
|
@ -410,7 +417,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.UPROAR}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
soundBased(soundBased: boolean = true): this { // TODO: is `true` the correct default?
|
||||
soundBased(soundBased: boolean = true): this {
|
||||
this.setFlag(MoveFlags.SOUND_BASED, soundBased);
|
||||
return this;
|
||||
}
|
||||
|
@ -421,7 +428,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.TELEPORT}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
hidesUser(hidesUser: boolean = true): this { // TODO: is `true` the correct default?
|
||||
hidesUser(hidesUser: boolean = true): this {
|
||||
this.setFlag(MoveFlags.HIDE_USER, hidesUser);
|
||||
return this;
|
||||
}
|
||||
|
@ -432,7 +439,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.WHIRLWIND}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
hidesTarget(hidesTarget: boolean = true): this { // TODO: is `true` the correct default?
|
||||
hidesTarget(hidesTarget: boolean = true): this {
|
||||
this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget);
|
||||
return this;
|
||||
}
|
||||
|
@ -443,7 +450,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.BITE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
bitingMove(bitingMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
bitingMove(bitingMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.BITING_MOVE, bitingMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -454,7 +461,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.WATER_PULSE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
pulseMove(pulseMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
pulseMove(pulseMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.PULSE_MOVE, pulseMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -465,7 +472,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.DRAIN_PUNCH}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
punchingMove(punchingMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
punchingMove(punchingMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -476,7 +483,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.X_SCISSOR}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
slicingMove(slicingMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
slicingMove(slicingMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.SLICING_MOVE, slicingMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -487,7 +494,7 @@ export default class Move implements Localizable {
|
|||
* @param recklessMove The value to set the flag to
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
recklessMove(recklessMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
recklessMove(recklessMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -498,7 +505,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.ELECTRO_BALL}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
ballBombMove(ballBombMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
ballBombMove(ballBombMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -509,7 +516,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.STUN_SPORE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
powderMove(powderMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
powderMove(powderMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.POWDER_MOVE, powderMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -520,7 +527,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.PETAL_DANCE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
danceMove(danceMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
danceMove(danceMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.DANCE_MOVE, danceMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -531,7 +538,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.HURRICANE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
windMove(windMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
windMove(windMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.WIND_MOVE, windMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -542,7 +549,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.ABSORB}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
triageMove(triageMove: boolean = true): this { // TODO: is `true` the correct default?
|
||||
triageMove(triageMove: boolean = true): this {
|
||||
this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove);
|
||||
return this;
|
||||
}
|
||||
|
@ -553,7 +560,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.SUNSTEEL_STRIKE}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
ignoresAbilities(ignoresAbilities: boolean = true): this { // TODO: is `true` the correct default?
|
||||
ignoresAbilities(ignoresAbilities: boolean = true): this {
|
||||
this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities);
|
||||
return this;
|
||||
}
|
||||
|
@ -564,7 +571,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.TRIPLE_AXEL}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
checkAllHits(checkAllHits: boolean = true): this { // TODO: is `true` the correct default?
|
||||
checkAllHits(checkAllHits: boolean = true): this {
|
||||
this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits);
|
||||
return this;
|
||||
}
|
||||
|
@ -575,7 +582,7 @@ export default class Move implements Localizable {
|
|||
* example: @see {@linkcode Moves.METAL_BURST}
|
||||
* @returns The {@linkcode Move} that called this function
|
||||
*/
|
||||
redirectCounter(redirectCounter: boolean = true): this { // TODO: is `true` the correct default?
|
||||
redirectCounter(redirectCounter: boolean = true): this {
|
||||
this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter);
|
||||
return this;
|
||||
}
|
||||
|
@ -2779,28 +2786,26 @@ export class ResetStatsAttr extends MoveEffectAttr {
|
|||
super();
|
||||
this.targetAllPokemon = targetAllPokemon;
|
||||
}
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(user, target, move, args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
const promises: Promise<void>[] = [];
|
||||
if (this.targetAllPokemon) { // Target all pokemon on the field when Freezy Frost or Haze are used
|
||||
const activePokemon = user.scene.getField(true);
|
||||
activePokemon.forEach(p => this.resetStats(p));
|
||||
activePokemon.forEach(p => promises.push(this.resetStats(p)));
|
||||
target.scene.queueMessage(i18next.t("moveTriggers:statEliminated"));
|
||||
} else { // Affects only the single target when Clear Smog is used
|
||||
this.resetStats(target);
|
||||
promises.push(this.resetStats(target));
|
||||
target.scene.queueMessage(i18next.t("moveTriggers:resetStats", {pokemonName: getPokemonNameWithAffix(target)}));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
return true;
|
||||
}
|
||||
|
||||
resetStats(pokemon: Pokemon) {
|
||||
async resetStats(pokemon: Pokemon): Promise<void> {
|
||||
for (const s of BATTLE_STATS) {
|
||||
pokemon.setStatStage(s, 0);
|
||||
}
|
||||
pokemon.updateInfo();
|
||||
return pokemon.updateInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4747,7 +4752,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) {
|
||||
if ((move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) && user.getLastXMoves(1)[0].result === MoveResult.SUCCESS) {
|
||||
user.scene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||
return true;
|
||||
}
|
||||
|
@ -7138,6 +7143,7 @@ export function initMoves() {
|
|||
new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK ], -2),
|
||||
new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2)
|
||||
.partial()
|
||||
.attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL),
|
||||
new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2)
|
||||
.attr(SurviveDamageAttr),
|
||||
|
@ -7417,9 +7423,11 @@ export function initMoves() {
|
|||
.attr(HighCritAttr)
|
||||
.attr(StatusEffectAttr, StatusEffect.BURN),
|
||||
new StatusMove(Moves.MUD_SPORT, Type.GROUND, -1, 15, -1, 0, 3)
|
||||
.ignoresProtect()
|
||||
.attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new AttackMove(Moves.ICE_BALL, Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 3)
|
||||
.partial()
|
||||
.attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL)
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3)
|
||||
|
@ -7541,6 +7549,7 @@ export function initMoves() {
|
|||
.recklessMove(),
|
||||
new AttackMove(Moves.MAGICAL_LEAF, Type.GRASS, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 3),
|
||||
new StatusMove(Moves.WATER_SPORT, Type.WATER, -1, 15, -1, 0, 3)
|
||||
.ignoresProtect()
|
||||
.attr(AddArenaTagAttr, ArenaTagType.WATER_SPORT, 5)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new SelfStatusMove(Moves.CALM_MIND, Type.PSYCHIC, -1, 20, -1, 0, 3)
|
||||
|
@ -7569,6 +7578,7 @@ export function initMoves() {
|
|||
.attr(AddBattlerTagAttr, BattlerTagType.ROOSTED, true, false)
|
||||
.triageMove(),
|
||||
new StatusMove(Moves.GRAVITY, Type.PSYCHIC, -1, 5, -1, 0, 4)
|
||||
.ignoresProtect()
|
||||
.attr(AddArenaTagAttr, ArenaTagType.GRAVITY, 5)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new StatusMove(Moves.MIRACLE_EYE, Type.PSYCHIC, -1, 40, -1, 0, 4)
|
||||
|
@ -8012,7 +8022,15 @@ export function initMoves() {
|
|||
new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5)
|
||||
.attr(CopyTypeAttr),
|
||||
new AttackMove(Moves.RETALIATE, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, 0, 5)
|
||||
.partial(),
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => {
|
||||
const turn = user.scene.currentBattle.turn;
|
||||
const lastPlayerFaint = user.scene.currentBattle.playerFaintsHistory[user.scene.currentBattle.playerFaintsHistory.length - 1];
|
||||
const lastEnemyFaint = user.scene.currentBattle.enemyFaintsHistory[user.scene.currentBattle.enemyFaintsHistory.length - 1];
|
||||
return (
|
||||
(lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) ||
|
||||
(lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && !user.isPlayer())
|
||||
) ? 2 : 1;
|
||||
}),
|
||||
new AttackMove(Moves.FINAL_GAMBIT, Type.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 5)
|
||||
.attr(UserHpDamageAttr)
|
||||
.attr(SacrificialAttrOnHit),
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { PokemonFormChangeItemModifier } from "../modifier/modifier";
|
||||
import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier";
|
||||
import Pokemon from "../field/pokemon";
|
||||
import { SpeciesFormKey } from "./pokemon-species";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import { MoveCategory, allMoves } from "./move";
|
||||
import { Type } from "./type";
|
||||
import { Constructor } from "#app/utils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
|
@ -357,6 +358,41 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used for triggering form changes based on the user's Tera type.
|
||||
* Used by Ogerpon and Terapagos.
|
||||
* @extends SpeciesFormChangeTrigger
|
||||
*/
|
||||
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
|
||||
/** The Tera type that triggers the form change */
|
||||
private teraType: Type;
|
||||
|
||||
constructor(teraType: Type) {
|
||||
super();
|
||||
this.teraType = teraType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the associated Pokémon has the required Tera Shard that matches with the associated Tera type.
|
||||
* @param {Pokemon} pokemon the Pokémon that is trying to do the form change
|
||||
* @returns `true` if the Pokémon can change forms, `false` otherwise
|
||||
*/
|
||||
canChange(pokemon: Pokemon): boolean {
|
||||
return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id && m.teraType === this.teraType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used for triggering form changes based on the user's lapsed Tera type.
|
||||
* Used by Ogerpon and Terapagos.
|
||||
* @extends SpeciesFormChangeTrigger
|
||||
*/
|
||||
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
|
||||
canChange(pokemon: Pokemon): boolean {
|
||||
return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used for triggering form changes based on weather.
|
||||
* Used by Castform and Cherrim.
|
||||
|
@ -592,6 +628,23 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
[Species.ALTARIA]: [
|
||||
new SpeciesFormChange(Species.ALTARIA, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.ALTARIANITE))
|
||||
],
|
||||
[Species.CASTFORM]: [
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true)
|
||||
],
|
||||
[Species.BANETTE]: [
|
||||
new SpeciesFormChange(Species.BANETTE, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.BANETTITE))
|
||||
],
|
||||
|
@ -627,6 +680,11 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
new SpeciesFormChange(Species.DEOXYS, "normal", "defense", new SpeciesFormChangeItemTrigger(FormChangeItem.HARD_METEORITE)),
|
||||
new SpeciesFormChange(Species.DEOXYS, "normal", "speed", new SpeciesFormChangeItemTrigger(FormChangeItem.SMOOTH_METEORITE))
|
||||
],
|
||||
[Species.CHERRIM]: [
|
||||
new SpeciesFormChange(Species.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(Abilities.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true)
|
||||
],
|
||||
[Species.LOPUNNY]: [
|
||||
new SpeciesFormChange(Species.LOPUNNY, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.LOPUNNITE))
|
||||
],
|
||||
|
@ -822,6 +880,14 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
[Species.SANDACONDA]: [
|
||||
new SpeciesFormChange(Species.SANDACONDA, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
],
|
||||
[Species.CRAMORANT]: [
|
||||
new SpeciesFormChange(Species.CRAMORANT, "", "gulping", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() >= .5)),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true)
|
||||
],
|
||||
[Species.TOXTRICITY]: [
|
||||
new SpeciesFormChange(Species.TOXTRICITY, "amped", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
|
||||
new SpeciesFormChange(Species.TOXTRICITY, "lowkey", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
|
||||
|
@ -848,6 +914,10 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
new SpeciesFormChange(Species.ALCREMIE, "caramel-swirl", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
|
||||
new SpeciesFormChange(Species.ALCREMIE, "rainbow-swirl", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
],
|
||||
[Species.EISCUE]: [
|
||||
new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.EISCUE, "no-ice", "", new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.MORPEKO]: [
|
||||
new SpeciesFormChange(Species.MORPEKO, "full-belly", "hangry", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MORPEKO, "hangry", "full-belly", new SpeciesFormChangeManualTrigger(), true)
|
||||
|
@ -883,58 +953,24 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
|||
new SpeciesFormChange(Species.OGERPON, "teal-mask", "wellspring-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.WELLSPRING_MASK)),
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask", "hearthflame-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.HEARTHFLAME_MASK)),
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask", "cornerstone-mask", new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)),
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Grass Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Grass Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Water Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Water Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Fire Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Fire Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeManualTrigger(), true), //When holding a Rock Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeManualTrigger(), true) //When no longer holding a Rock Tera Shard
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask", "teal-mask-tera", new SpeciesFormChangeTeraTrigger(Type.GRASS)),
|
||||
new SpeciesFormChange(Species.OGERPON, "teal-mask-tera", "teal-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.GRASS)),
|
||||
new SpeciesFormChange(Species.OGERPON, "wellspring-mask", "wellspring-mask-tera", new SpeciesFormChangeTeraTrigger(Type.WATER)),
|
||||
new SpeciesFormChange(Species.OGERPON, "wellspring-mask-tera", "wellspring-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.WATER)),
|
||||
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask", "hearthflame-mask-tera", new SpeciesFormChangeTeraTrigger(Type.FIRE)),
|
||||
new SpeciesFormChange(Species.OGERPON, "hearthflame-mask-tera", "hearthflame-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.FIRE)),
|
||||
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask", "cornerstone-mask-tera", new SpeciesFormChangeTeraTrigger(Type.ROCK)),
|
||||
new SpeciesFormChange(Species.OGERPON, "cornerstone-mask-tera", "cornerstone-mask", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.ROCK))
|
||||
],
|
||||
[Species.TERAPAGOS]: [
|
||||
new SpeciesFormChange(Species.TERAPAGOS, "", "terastal", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeManualTrigger(), true), //When holding a Stellar Tera Shard
|
||||
new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeManualTrigger(), true) //When no longer holding a Stellar Tera Shard
|
||||
new SpeciesFormChange(Species.TERAPAGOS, "terastal", "stellar", new SpeciesFormChangeTeraTrigger(Type.STELLAR)),
|
||||
new SpeciesFormChange(Species.TERAPAGOS, "stellar", "terastal", new SpeciesFormChangeLapseTeraTrigger(), true, new SpeciesFormChangeCondition(p => p.getTeraType() !== Type.STELLAR))
|
||||
],
|
||||
[Species.GALAR_DARMANITAN]: [
|
||||
new SpeciesFormChange(Species.GALAR_DARMANITAN, "", "zen", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.GALAR_DARMANITAN, "zen", "", new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.EISCUE]: [
|
||||
new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.EISCUE, "no-ice", "", new SpeciesFormChangeManualTrigger(), true),
|
||||
],
|
||||
[Species.CRAMORANT]: [
|
||||
new SpeciesFormChange(Species.CRAMORANT, "", "gulping", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() >= .5)),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true),
|
||||
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true),
|
||||
],
|
||||
[Species.CASTFORM]: [
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "sunny", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.SUNNY, WeatherType.HARSH_SUN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "rainy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "snowy", new SpeciesFormChangeWeatherTrigger(Abilities.FORECAST, [WeatherType.HAIL, WeatherType.SNOW]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FORECAST, [WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG]), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "sunny", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "rainy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
new SpeciesFormChange(Species.CASTFORM, "snowy", "", new SpeciesFormChangeActiveTrigger(), true),
|
||||
],
|
||||
[Species.CHERRIM]: [
|
||||
new SpeciesFormChange(Species.CHERRIM, "overcast", "sunshine", new SpeciesFormChangeWeatherTrigger(Abilities.FLOWER_GIFT, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeRevertWeatherFormTrigger(Abilities.FLOWER_GIFT, [ WeatherType.NONE, WeatherType.SANDSTORM, WeatherType.STRONG_WINDS, WeatherType.FOG, WeatherType.HAIL, WeatherType.HEAVY_RAIN, WeatherType.SNOW, WeatherType.RAIN ]), true),
|
||||
new SpeciesFormChange(Species.CHERRIM, "sunshine", "overcast", new SpeciesFormChangeActiveTrigger(), true),
|
||||
],
|
||||
};
|
||||
|
||||
export function initPokemonForms() {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { BerryType } from "#enums/berry-type";
|
|||
import { StatusEffect, getStatusEffectHealText } from "../data/status-effect";
|
||||
import { achvs } from "../system/achv";
|
||||
import { VoucherType } from "../system/voucher";
|
||||
import { FormChangeItem, SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
|
||||
import { FormChangeItem, SpeciesFormChangeItemTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeTeraTrigger } from "../data/pokemon-forms";
|
||||
import { Nature } from "#app/data/nature";
|
||||
import Overrides from "#app/overrides";
|
||||
import { ModifierType, modifierTypes } from "./modifier-type";
|
||||
|
@ -29,6 +29,7 @@ import { Abilities } from "#app/enums/abilities";
|
|||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { LevelUpPhase } from "#app/phases/level-up-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { SpeciesFormKey } from "#app/data/pokemon-species";
|
||||
|
||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||
|
||||
|
@ -762,6 +763,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
|
|||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
if (pokemon.isPlayer()) {
|
||||
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeTeraTrigger);
|
||||
pokemon.scene.validateAchv(achvs.TERASTALLIZE);
|
||||
if (this.teraType === Type.STELLAR) {
|
||||
pokemon.scene.validateAchv(achvs.STELLAR_TERASTALLIZE);
|
||||
|
@ -775,6 +777,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
|
|||
const ret = super.lapse(args);
|
||||
if (!ret) {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeLapseTeraTrigger);
|
||||
pokemon.updateSpritePipelineData();
|
||||
}
|
||||
return ret;
|
||||
|
@ -923,6 +926,18 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier {
|
|||
return modifier instanceof EvolutionStatBoosterModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the stat boosts can apply and if the holder is not currently
|
||||
* Gigantamax'd.
|
||||
* @param args [0] {@linkcode Pokemon} that holds the held item
|
||||
* [1] {@linkcode Stat} N/A
|
||||
* [2] {@linkcode Utils.NumberHolder} N/A
|
||||
* @returns true if the stat boosts can be applied, false otherwise
|
||||
*/
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && ((args[0] as Pokemon).getFormKey() !== SpeciesFormKey.GIGANTAMAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boosts the incoming stat value by a {@linkcode multiplier} if the holder
|
||||
* can evolve. Note that, if the holder is a fusion, they will receive
|
||||
|
|
|
@ -55,8 +55,10 @@ export class FaintPhase extends PokemonPhase {
|
|||
// Track total times pokemon have been KO'd for supreme overlord/last respects
|
||||
if (pokemon.isPlayer()) {
|
||||
this.scene.currentBattle.playerFaints += 1;
|
||||
this.scene.currentBattle.playerFaintsHistory.push({ pokemon: pokemon, turn: this.scene.currentBattle.turn });
|
||||
} else {
|
||||
this.scene.currentBattle.enemyFaints += 1;
|
||||
this.scene.currentBattle.enemyFaintsHistory.push({ pokemon: pokemon, turn: this.scene.currentBattle.turn });
|
||||
}
|
||||
|
||||
this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { BattlerIndex } from "#app/battle";
|
|||
import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability";
|
||||
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
|
||||
import { MoveAnim } from "#app/data/battle-anims";
|
||||
import { BattlerTagLapseType, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags";
|
||||
import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags";
|
||||
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move";
|
||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
|
@ -152,7 +152,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
|
||||
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
|
||||
const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target))
|
||||
&& (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)));
|
||||
&& (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)))
|
||||
|| (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
|
||||
|
||||
/** Does this phase represent the invoked move's first strike? */
|
||||
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { Stat } from "#enums/stat";
|
||||
import { EvolutionStatBoosterModifier } from "#app/modifier/modifier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import * as Utils from "#app/utils";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phase from "phaser";
|
||||
import * as Utils from "#app/utils";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { StatBoosterModifier } from "#app/modifier/modifier";
|
||||
|
||||
describe("Items - Eviolite", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phase.Game({
|
||||
|
@ -25,108 +24,65 @@ describe("Items - Eviolite", () => {
|
|||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override.battleType("single");
|
||||
game.override
|
||||
.battleType("single")
|
||||
.startingHeldItems([{ name: "EVIOLITE" }]);
|
||||
});
|
||||
|
||||
it("EVIOLITE activates in battle correctly", async() => {
|
||||
game.override.startingHeldItems([{ name: "EVIOLITE" }]);
|
||||
const consoleSpy = vi.spyOn(console, "log");
|
||||
await game.startBattle([
|
||||
it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.PICHU
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
|
||||
// Checking console log to make sure Eviolite is applied when getEffectiveStat (with the appropriate stat) is called
|
||||
partyMember.getEffectiveStat(Stat.DEF);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Printing dummy console messages along the way so subsequent checks don't pass because of the first
|
||||
console.log("");
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
partyMember.getEffectiveStat(Stat.SPDEF);
|
||||
expect(consoleSpy).toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getEffectiveStat(Stat.ATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getEffectiveStat(Stat.SPATK);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
|
||||
console.log("");
|
||||
|
||||
partyMember.getEffectiveStat(Stat.SPD);
|
||||
expect(consoleSpy).not.toHaveBeenLastCalledWith("Applied", i18next.t("modifierType:ModifierType.EVIOLITE.name"), "");
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
it("EVIOLITE held by unevolved, unfused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
Species.PICHU
|
||||
]);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5));
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5));
|
||||
}, TIMEOUT);
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1.5);
|
||||
expect(spDefValue.value / spDefStat).toBe(1.5);
|
||||
}, 20000);
|
||||
|
||||
it("EVIOLITE held by fully evolved, unfused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
it("should not provide a boost for fully evolved, unfused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.RAICHU,
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
}, 20000);
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat);
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat);
|
||||
|
||||
it("EVIOLITE held by completely unevolved, fused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should provide 50% boost to DEF and SPDEF for completely unevolved, fused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.PICHU,
|
||||
Species.CLEFFA
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
const ally = game.scene.getParty()[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
|
@ -137,35 +93,29 @@ describe("Items - Eviolite", () => {
|
|||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1.5);
|
||||
expect(spDefValue.value / spDefStat).toBe(1.5);
|
||||
}, 20000);
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.5));
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.5));
|
||||
}, TIMEOUT);
|
||||
|
||||
it("EVIOLITE held by partially unevolved (base), fused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
it("should provide 25% boost to DEF and SPDEF for partially unevolved (base), fused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.PICHU,
|
||||
Species.CLEFABLE
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
const ally = game.scene.getParty()[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
|
@ -176,35 +126,29 @@ describe("Items - Eviolite", () => {
|
|||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1.25);
|
||||
expect(spDefValue.value / spDefStat).toBe(1.25);
|
||||
}, 20000);
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25));
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25));
|
||||
}, TIMEOUT);
|
||||
|
||||
it("EVIOLITE held by partially unevolved (fusion), fused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
it("should provide 25% boost to DEF and SPDEF for partially unevolved (fusion), fused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.RAICHU,
|
||||
Species.CLEFFA
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
const ally = game.scene.getParty()[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
|
@ -215,35 +159,29 @@ describe("Items - Eviolite", () => {
|
|||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1.25);
|
||||
expect(spDefValue.value / spDefStat).toBe(1.25);
|
||||
}, 20000);
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(Math.floor(defStat * 1.25));
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(spDefStat * 1.25));
|
||||
}, TIMEOUT);
|
||||
|
||||
it("EVIOLITE held by completely evolved, fused pokemon", async() => {
|
||||
await game.startBattle([
|
||||
it("should not provide a boost for fully evolved, fused pokemon", async() => {
|
||||
await game.classicMode.startBattle([
|
||||
Species.RAICHU,
|
||||
Species.CLEFABLE
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getParty()[0];
|
||||
const ally = game.scene.getParty()[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
|
@ -254,24 +192,51 @@ describe("Items - Eviolite", () => {
|
|||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF);
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const defValue = new Utils.NumberHolder(defStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
const spDefValue = new Utils.NumberHolder(spDefStat);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
// Giving Eviolite to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.EVIOLITE().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
|
||||
partyMember.scene.applyModifiers(EvolutionStatBoosterModifier, true, partyMember, Stat.SPDEF, spDefValue);
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(defValue.value / defStat).toBe(1);
|
||||
expect(spDefValue.value / spDefStat).toBe(1);
|
||||
}, 20000);
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat);
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should not provide a boost for Gigantamax Pokémon", async() => {
|
||||
game.override.starterForms({
|
||||
[Species.PIKACHU]: 8,
|
||||
[Species.EEVEE]: 2,
|
||||
[Species.DURALUDON]: 1,
|
||||
[Species.MEOWTH]: 1
|
||||
});
|
||||
|
||||
const gMaxablePokemon = [ Species.PIKACHU, Species.EEVEE, Species.DURALUDON, Species.MEOWTH ];
|
||||
|
||||
await game.classicMode.startBattle([
|
||||
Utils.randItem(gMaxablePokemon)
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
|
||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||
|
||||
// Ignore other calculations for simplicity
|
||||
|
||||
return Math.floor(statValue.value);
|
||||
});
|
||||
|
||||
const defStat = partyMember.getStat(Stat.DEF, false);
|
||||
const spDefStat = partyMember.getStat(Stat.SPDEF, false);
|
||||
|
||||
expect(partyMember.getEffectiveStat(Stat.DEF)).toBe(defStat);
|
||||
expect(partyMember.getEffectiveStat(Stat.SPDEF)).toBe(spDefStat);
|
||||
}, TIMEOUT);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
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, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Baddy Bad", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([Moves.SPLASH])
|
||||
.battleType("single")
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should not activate Reflect if the move fails due to Protect", async () => {
|
||||
game.override.enemyMoveset(Moves.PROTECT);
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
|
||||
game.move.select(Moves.BADDY_BAD);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.arena.tags.length).toBe(0);
|
||||
}, TIMEOUT);
|
||||
});
|
|
@ -1,12 +1,12 @@
|
|||
import { Stat } from "#enums/stat";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
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, expect, it, vi } from "vitest";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
|
||||
describe("Moves - Freezy Frost", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -23,38 +23,83 @@ describe("Moves - Freezy Frost", () => {
|
|||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override.battleType("single");
|
||||
game.override
|
||||
.battleType("single")
|
||||
.enemySpecies(Species.RATTATA)
|
||||
.enemyLevel(100)
|
||||
.enemyMoveset([ Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL ])
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.startingLevel(100)
|
||||
.moveset([ Moves.FREEZY_FROST, Moves.HOWL, Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
|
||||
game.override.enemySpecies(Species.RATTATA);
|
||||
game.override.enemyLevel(100);
|
||||
game.override.enemyMoveset(Moves.SPLASH);
|
||||
game.override.enemyAbility(Abilities.NONE);
|
||||
|
||||
game.override.startingLevel(100);
|
||||
game.override.moveset([Moves.FREEZY_FROST, Moves.SWORDS_DANCE, Moves.CHARM, Moves.SPLASH]);
|
||||
vi.spyOn(allMoves[Moves.FREEZY_FROST], "accuracy", "get").mockReturnValue(100);
|
||||
game.override.ability(Abilities.NONE);
|
||||
vi.spyOn(allMoves[ Moves.FREEZY_FROST ], "accuracy", "get").mockReturnValue(100);
|
||||
});
|
||||
|
||||
it("should clear all stat stage changes", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.RATTATA]);
|
||||
it(
|
||||
"should clear stat changes of user and opponent",
|
||||
async () => {
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
game.move.select(Moves.HOWL);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(Moves.SWORDS_DANCE);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
game.move.select(Moves.CHARM);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(2);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(-2);
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(1);
|
||||
|
||||
game.move.select(Moves.FREEZY_FROST);
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemy.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it(
|
||||
"should clear all stat changes even when enemy uses the move",
|
||||
async () => {
|
||||
game.override.enemyMoveset([ Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST, Moves.FREEZY_FROST ]);
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE ]); // Shuckle for slower Howl on first turn so Freezy Frost doesn't affect it.
|
||||
const user = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(Moves.HOWL);
|
||||
await game.toNextTurn();
|
||||
|
||||
const userAtkBefore = user.getStatStage(Stat.ATK);
|
||||
expect(userAtkBefore).toBe(1);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
expect(user.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it(
|
||||
"should clear all stat changes in double battle",
|
||||
async () => {
|
||||
game.override.battleType("double");
|
||||
await game.classicMode.startBattle([ Species.SHUCKLE, Species.RATTATA ]);
|
||||
const [ leftPlayer, rightPlayer ] = game.scene.getPlayerField();
|
||||
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||
|
||||
game.move.select(Moves.HOWL, 0);
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(rightPlayer.getStatStage(Stat.ATK)).toBe(1);
|
||||
expect(leftOpp.getStatStage(Stat.ATK)).toBe(2); // Both enemies use Howl
|
||||
expect(rightOpp.getStatStage(Stat.ATK)).toBe(2);
|
||||
|
||||
game.move.select(Moves.FREEZY_FROST, 0, leftOpp.getBattlerIndex());
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(leftPlayer.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(rightPlayer.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(leftOpp.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(rightOpp.getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import { Moves } from "#app/enums/moves";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Obstruct", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
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)
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.moveset([Moves.OBSTRUCT]);
|
||||
});
|
||||
|
||||
it("protects from contact damaging moves and lowers the opponent's defense by 2 stages", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH));
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
game.move.select(Moves.OBSTRUCT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(player.isFullHp()).toBe(true);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(-2);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN));
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
game.move.select(Moves.OBSTRUCT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(player.isFullHp()).toBe(true);
|
||||
expect(enemy.getStatStage(Stat.DEF)).toBe(0);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("doesn't protect from status moves", async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.GROWL));
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
game.move.select(Moves.OBSTRUCT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(player.getStatStage(Stat.ATK)).toBe(-1);
|
||||
}, TIMEOUT);
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import { Species } from "#enums/species";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { allMoves } from "#app/data/move";
|
||||
|
||||
describe("Moves - Retaliate", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
const retaliate = allMoves[Moves.RETALIATE];
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleType("single")
|
||||
.enemySpecies(Species.SNORLAX)
|
||||
.enemyMoveset([Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE])
|
||||
.enemyLevel(100)
|
||||
.moveset([Moves.RETALIATE, Moves.SPLASH])
|
||||
.startingLevel(80)
|
||||
.disableCrits();
|
||||
});
|
||||
|
||||
it("increases power if ally died previous turn", async () => {
|
||||
vi.spyOn(retaliate, "calculateBattlePower");
|
||||
await game.startBattle([Species.ABRA, Species.COBALION]);
|
||||
game.move.select(Moves.RETALIATE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(retaliate.calculateBattlePower).toHaveLastReturnedWith(70);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.toNextTurn();
|
||||
game.move.select(Moves.RETALIATE);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
expect(retaliate.calculateBattlePower).toHaveReturnedWith(140);
|
||||
});
|
||||
});
|
|
@ -541,7 +541,9 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
// Contains Name, Level + Nature, Ability, Passive
|
||||
const pokeInfoTextContainer = this.scene.add.container(-85, 3.5);
|
||||
const textContainerFontSize = "34px";
|
||||
const pNature = getNatureName(pokemon.nature);
|
||||
// This checks if the Pokemon's nature has been overwritten during the run and displays the change accurately
|
||||
const pNature = pokemon.getNature();
|
||||
const pNatureName = getNatureName(pNature);
|
||||
const pName = pokemon.getNameToRender();
|
||||
//With the exception of Korean/Traditional Chinese/Simplified Chinese, the code shortens the terms for ability and passive to their first letter.
|
||||
//These languages are exempted because they are already short enough.
|
||||
|
@ -557,7 +559,7 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
// Japanese is set to a greater line spacing of 35px in addBBCodeTextObject() if lineSpacing < 12.
|
||||
const lineSpacing = (i18next.resolvedLanguage === "ja") ? 12 : 3;
|
||||
const pokeInfoText = addBBCodeTextObject(this.scene, 0, 0, pName, TextStyle.SUMMARY, {fontSize: textContainerFontSize, lineSpacing: lineSpacing});
|
||||
pokeInfoText.appendText(`${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatFancyLargeNumber(pokemon.level, 1)} - ${pNature}`);
|
||||
pokeInfoText.appendText(`${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatFancyLargeNumber(pokemon.level, 1)} - ${pNatureName}`);
|
||||
pokeInfoText.appendText(pAbilityInfo);
|
||||
pokeInfoText.appendText(pPassiveInfo);
|
||||
pokeInfoTextContainer.add(pokeInfoText);
|
||||
|
@ -568,7 +570,7 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
const pStats : string[]= [];
|
||||
pokemon.stats.forEach((element) => pStats.push(Utils.formatFancyLargeNumber(element, 1)));
|
||||
for (let i = 0; i < pStats.length; i++) {
|
||||
const isMult = getNatureStatMultiplier(pokemon.nature, i);
|
||||
const isMult = getNatureStatMultiplier(pNature, i);
|
||||
pStats[i] = (isMult < 1) ? pStats[i] + "[color=#40c8f8]↓[/color]" : pStats[i];
|
||||
pStats[i] = (isMult > 1) ? pStats[i] + "[color=#f89890]↑[/color]" : pStats[i];
|
||||
}
|
||||
|
@ -889,11 +891,13 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
}
|
||||
break;
|
||||
case Button.CYCLE_ABILITY:
|
||||
if (this.runInfo.modifiers.length !== 0) {
|
||||
if (this.partyVisibility) {
|
||||
this.showParty(false);
|
||||
} else {
|
||||
this.showParty(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,15 @@ import { Mode } from "./ui";
|
|||
import { addWindow } from "./ui-theme";
|
||||
import { Egg } from "#app/data/egg";
|
||||
import Overrides from "#app/overrides";
|
||||
import {SettingKeyboard} from "#app/system/settings/settings-keyboard";
|
||||
import {Passive as PassiveAttr} from "#enums/passive";
|
||||
import { SettingKeyboard } from "#app/system/settings/settings-keyboard";
|
||||
import { Passive as PassiveAttr } from "#enums/passive";
|
||||
import * as Challenge from "../data/challenge";
|
||||
import MoveInfoOverlay from "./move-info-overlay";
|
||||
import { getEggTierForSpecies } from "#app/data/egg";
|
||||
import { Device } from "#enums/devices";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import {Button} from "#enums/buttons";
|
||||
import { Button } from "#enums/buttons";
|
||||
import { EggSourceType } from "#app/enums/egg-source-types";
|
||||
import AwaitableUiHandler from "./awaitable-ui-handler";
|
||||
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "./dropdown";
|
||||
|
@ -2905,7 +2905,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
}
|
||||
|
||||
const speciesForm = getPokemonSpeciesForm(species.speciesId, props.formIndex);
|
||||
this.setTypeIcons(speciesForm.type1, speciesForm!.type2!); // TODO: are those bangs correct?
|
||||
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
|
||||
|
||||
this.pokemonSprite.clearTint();
|
||||
if (this.pokerusSpecies.includes(species)) {
|
||||
|
@ -3242,13 +3242,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.pokemonFormText.setText(formText ? i18next.t(`pokemonForm:${speciesName}${formText}`) : "");
|
||||
}
|
||||
|
||||
this.setTypeIcons(speciesForm.type1, speciesForm.type2!); // TODO: is this bang correct?
|
||||
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
|
||||
} else {
|
||||
this.pokemonAbilityText.setText("");
|
||||
this.pokemonPassiveText.setText("");
|
||||
this.pokemonNatureText.setText("");
|
||||
// @ts-ignore
|
||||
this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!?
|
||||
this.setTypeIcons(null, null);
|
||||
}
|
||||
} else {
|
||||
this.shinyOverlay.setVisible(false);
|
||||
|
@ -3258,8 +3257,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.pokemonAbilityText.setText("");
|
||||
this.pokemonPassiveText.setText("");
|
||||
this.pokemonNatureText.setText("");
|
||||
// @ts-ignore
|
||||
this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!?
|
||||
this.setTypeIcons(null, null);
|
||||
}
|
||||
|
||||
if (!this.starterMoveset) {
|
||||
|
@ -3292,7 +3290,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.updateInstructions();
|
||||
}
|
||||
|
||||
setTypeIcons(type1: Type, type2: Type): void {
|
||||
setTypeIcons(type1: Type | null, type2: Type | null): void {
|
||||
if (type1 !== null) {
|
||||
this.type1Icon.setVisible(true);
|
||||
this.type1Icon.setFrame(Type[type1].toLowerCase());
|
||||
|
|
Loading…
Reference in New Issue