Merge branch 'beta' of https://github.com/pagefaultgames/pokerogue into svteams
@ -170,7 +170,7 @@ input:-internal-autofill-selected {
|
|||||||
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadCycleNature,
|
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT']) #apadCycleNature,
|
||||||
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE'], [data-ui-mode='RUN_INFO']) #apadCycleAbility,
|
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE'], [data-ui-mode='RUN_INFO']) #apadCycleAbility,
|
||||||
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE']) #apadCycleGender,
|
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX_PAGE']) #apadCycleGender,
|
||||||
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX']) #apadCycleVariant {
|
#touchControls:not(.config-mode):not([data-ui-mode='STARTER_SELECT'], [data-ui-mode='POKEDEX']) #apadCycleTera {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
<div id="apadCycleNature" class="apad-button apad-square apad-small" data-key="CYCLE_NATURE">
|
<div id="apadCycleNature" class="apad-button apad-square apad-small" data-key="CYCLE_NATURE">
|
||||||
<span class="apad-label">N</span>
|
<span class="apad-label">N</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="apadCycleVariant" class="apad-button apad-square apad-small" data-key="V">
|
<div id="apadCycleTera" class="apad-button apad-square apad-small" data-key="CYCLE_TERA">
|
||||||
<span class="apad-label">V</span>
|
<span class="apad-label">V</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- buttons to display battle-specific information -->
|
<!-- buttons to display battle-specific information -->
|
||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.9 KiB |
@ -1 +1 @@
|
|||||||
Subproject commit 58dda14ee834204c4bd5ece47694a3c068df4b0e
|
Subproject commit ef43efffe5fe454862c350f1b9393c3ad755bcc2
|
2175
src/battle-scene.ts
@ -180,9 +180,9 @@ const cfg_keyboard_qwerty = {
|
|||||||
[SettingKeyboard.Button_Cycle_Gender]: Button.CYCLE_GENDER,
|
[SettingKeyboard.Button_Cycle_Gender]: Button.CYCLE_GENDER,
|
||||||
[SettingKeyboard.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
|
[SettingKeyboard.Button_Cycle_Ability]: Button.CYCLE_ABILITY,
|
||||||
[SettingKeyboard.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingKeyboard.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingKeyboard.Button_Cycle_Variant]: Button.V,
|
|
||||||
[SettingKeyboard.Button_Speed_Up]: Button.SPEED_UP,
|
[SettingKeyboard.Button_Speed_Up]: Button.SPEED_UP,
|
||||||
[SettingKeyboard.Button_Slow_Down]: Button.SLOW_DOWN,
|
[SettingKeyboard.Button_Slow_Down]: Button.SLOW_DOWN,
|
||||||
|
[SettingKeyboard.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingKeyboard.Alt_Button_Up]: Button.UP,
|
[SettingKeyboard.Alt_Button_Up]: Button.UP,
|
||||||
[SettingKeyboard.Alt_Button_Down]: Button.DOWN,
|
[SettingKeyboard.Alt_Button_Down]: Button.DOWN,
|
||||||
[SettingKeyboard.Alt_Button_Left]: Button.LEFT,
|
[SettingKeyboard.Alt_Button_Left]: Button.LEFT,
|
||||||
@ -197,9 +197,9 @@ const cfg_keyboard_qwerty = {
|
|||||||
[SettingKeyboard.Alt_Button_Cycle_Gender]: Button.CYCLE_GENDER,
|
[SettingKeyboard.Alt_Button_Cycle_Gender]: Button.CYCLE_GENDER,
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Ability]: Button.CYCLE_ABILITY,
|
[SettingKeyboard.Alt_Button_Cycle_Ability]: Button.CYCLE_ABILITY,
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingKeyboard.Alt_Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Variant]: Button.V,
|
|
||||||
[SettingKeyboard.Alt_Button_Speed_Up]: Button.SPEED_UP,
|
[SettingKeyboard.Alt_Button_Speed_Up]: Button.SPEED_UP,
|
||||||
[SettingKeyboard.Alt_Button_Slow_Down]: Button.SLOW_DOWN,
|
[SettingKeyboard.Alt_Button_Slow_Down]: Button.SLOW_DOWN,
|
||||||
|
[SettingKeyboard.Alt_Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
KEY_ARROW_UP: SettingKeyboard.Button_Up,
|
KEY_ARROW_UP: SettingKeyboard.Button_Up,
|
||||||
@ -216,7 +216,7 @@ const cfg_keyboard_qwerty = {
|
|||||||
KEY_G: SettingKeyboard.Button_Cycle_Gender,
|
KEY_G: SettingKeyboard.Button_Cycle_Gender,
|
||||||
KEY_E: SettingKeyboard.Button_Cycle_Ability,
|
KEY_E: SettingKeyboard.Button_Cycle_Ability,
|
||||||
KEY_N: SettingKeyboard.Button_Cycle_Nature,
|
KEY_N: SettingKeyboard.Button_Cycle_Nature,
|
||||||
KEY_V: SettingKeyboard.Button_Cycle_Variant,
|
KEY_V: SettingKeyboard.Button_Cycle_Tera,
|
||||||
KEY_PLUS: -1,
|
KEY_PLUS: -1,
|
||||||
KEY_MINUS: -1,
|
KEY_MINUS: -1,
|
||||||
KEY_A: SettingKeyboard.Alt_Button_Left,
|
KEY_A: SettingKeyboard.Alt_Button_Left,
|
||||||
|
@ -53,7 +53,7 @@ const pad_dualshock = {
|
|||||||
[SettingGamepad.Button_Action]: Button.ACTION,
|
[SettingGamepad.Button_Action]: Button.ACTION,
|
||||||
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: Button.V,
|
[SettingGamepad.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingGamepad.Button_Menu]: Button.MENU,
|
[SettingGamepad.Button_Menu]: Button.MENU,
|
||||||
[SettingGamepad.Button_Stats]: Button.STATS,
|
[SettingGamepad.Button_Stats]: Button.STATS,
|
||||||
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
||||||
@ -72,7 +72,7 @@ const pad_dualshock = {
|
|||||||
RC_S: SettingGamepad.Button_Action,
|
RC_S: SettingGamepad.Button_Action,
|
||||||
RC_E: SettingGamepad.Button_Cancel,
|
RC_E: SettingGamepad.Button_Cancel,
|
||||||
RC_W: SettingGamepad.Button_Cycle_Nature,
|
RC_W: SettingGamepad.Button_Cycle_Nature,
|
||||||
RC_N: SettingGamepad.Button_Cycle_Variant,
|
RC_N: SettingGamepad.Button_Cycle_Tera,
|
||||||
START: SettingGamepad.Button_Menu,
|
START: SettingGamepad.Button_Menu,
|
||||||
SELECT: SettingGamepad.Button_Stats,
|
SELECT: SettingGamepad.Button_Stats,
|
||||||
LB: SettingGamepad.Button_Cycle_Form,
|
LB: SettingGamepad.Button_Cycle_Form,
|
||||||
|
@ -51,7 +51,7 @@ const pad_generic = {
|
|||||||
[SettingGamepad.Button_Action]: Button.ACTION,
|
[SettingGamepad.Button_Action]: Button.ACTION,
|
||||||
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: Button.V,
|
[SettingGamepad.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingGamepad.Button_Menu]: Button.MENU,
|
[SettingGamepad.Button_Menu]: Button.MENU,
|
||||||
[SettingGamepad.Button_Stats]: Button.STATS,
|
[SettingGamepad.Button_Stats]: Button.STATS,
|
||||||
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
||||||
@ -69,7 +69,7 @@ const pad_generic = {
|
|||||||
RC_S: SettingGamepad.Button_Action,
|
RC_S: SettingGamepad.Button_Action,
|
||||||
RC_E: SettingGamepad.Button_Cancel,
|
RC_E: SettingGamepad.Button_Cancel,
|
||||||
RC_W: SettingGamepad.Button_Cycle_Nature,
|
RC_W: SettingGamepad.Button_Cycle_Nature,
|
||||||
RC_N: SettingGamepad.Button_Cycle_Variant,
|
RC_N: SettingGamepad.Button_Cycle_Tera,
|
||||||
START: SettingGamepad.Button_Menu,
|
START: SettingGamepad.Button_Menu,
|
||||||
SELECT: SettingGamepad.Button_Stats,
|
SELECT: SettingGamepad.Button_Stats,
|
||||||
LB: SettingGamepad.Button_Cycle_Form,
|
LB: SettingGamepad.Button_Cycle_Form,
|
||||||
|
@ -52,7 +52,7 @@ const pad_procon = {
|
|||||||
[SettingGamepad.Button_Action]: Button.ACTION,
|
[SettingGamepad.Button_Action]: Button.ACTION,
|
||||||
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: Button.V,
|
[SettingGamepad.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingGamepad.Button_Menu]: Button.MENU,
|
[SettingGamepad.Button_Menu]: Button.MENU,
|
||||||
[SettingGamepad.Button_Stats]: Button.STATS,
|
[SettingGamepad.Button_Stats]: Button.STATS,
|
||||||
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
||||||
@ -70,7 +70,7 @@ const pad_procon = {
|
|||||||
RC_S: SettingGamepad.Button_Action,
|
RC_S: SettingGamepad.Button_Action,
|
||||||
RC_E: SettingGamepad.Button_Cancel,
|
RC_E: SettingGamepad.Button_Cancel,
|
||||||
RC_W: SettingGamepad.Button_Cycle_Nature,
|
RC_W: SettingGamepad.Button_Cycle_Nature,
|
||||||
RC_N: SettingGamepad.Button_Cycle_Variant,
|
RC_N: SettingGamepad.Button_Cycle_Tera,
|
||||||
START: SettingGamepad.Button_Menu,
|
START: SettingGamepad.Button_Menu,
|
||||||
SELECT: SettingGamepad.Button_Stats,
|
SELECT: SettingGamepad.Button_Stats,
|
||||||
LB: SettingGamepad.Button_Cycle_Form,
|
LB: SettingGamepad.Button_Cycle_Form,
|
||||||
|
@ -43,7 +43,7 @@ const pad_unlicensedSNES = {
|
|||||||
[SettingGamepad.Button_Action]: Button.ACTION,
|
[SettingGamepad.Button_Action]: Button.ACTION,
|
||||||
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: Button.V,
|
[SettingGamepad.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingGamepad.Button_Menu]: Button.MENU,
|
[SettingGamepad.Button_Menu]: Button.MENU,
|
||||||
[SettingGamepad.Button_Stats]: Button.STATS,
|
[SettingGamepad.Button_Stats]: Button.STATS,
|
||||||
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
||||||
@ -61,7 +61,7 @@ const pad_unlicensedSNES = {
|
|||||||
RC_S: SettingGamepad.Button_Action,
|
RC_S: SettingGamepad.Button_Action,
|
||||||
RC_E: SettingGamepad.Button_Cancel,
|
RC_E: SettingGamepad.Button_Cancel,
|
||||||
RC_W: SettingGamepad.Button_Cycle_Nature,
|
RC_W: SettingGamepad.Button_Cycle_Nature,
|
||||||
RC_N: SettingGamepad.Button_Cycle_Variant,
|
RC_N: SettingGamepad.Button_Cycle_Tera,
|
||||||
START: SettingGamepad.Button_Menu,
|
START: SettingGamepad.Button_Menu,
|
||||||
SELECT: SettingGamepad.Button_Stats,
|
SELECT: SettingGamepad.Button_Stats,
|
||||||
LB: SettingGamepad.Button_Cycle_Form,
|
LB: SettingGamepad.Button_Cycle_Form,
|
||||||
|
@ -51,7 +51,7 @@ const pad_xbox360 = {
|
|||||||
[SettingGamepad.Button_Action]: Button.ACTION,
|
[SettingGamepad.Button_Action]: Button.ACTION,
|
||||||
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
[SettingGamepad.Button_Cancel]: Button.CANCEL,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
[SettingGamepad.Button_Cycle_Nature]: Button.CYCLE_NATURE,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: Button.V,
|
[SettingGamepad.Button_Cycle_Tera]: Button.CYCLE_TERA,
|
||||||
[SettingGamepad.Button_Menu]: Button.MENU,
|
[SettingGamepad.Button_Menu]: Button.MENU,
|
||||||
[SettingGamepad.Button_Stats]: Button.STATS,
|
[SettingGamepad.Button_Stats]: Button.STATS,
|
||||||
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
[SettingGamepad.Button_Cycle_Form]: Button.CYCLE_FORM,
|
||||||
@ -69,7 +69,7 @@ const pad_xbox360 = {
|
|||||||
RC_S: SettingGamepad.Button_Action,
|
RC_S: SettingGamepad.Button_Action,
|
||||||
RC_E: SettingGamepad.Button_Cancel,
|
RC_E: SettingGamepad.Button_Cancel,
|
||||||
RC_W: SettingGamepad.Button_Cycle_Nature,
|
RC_W: SettingGamepad.Button_Cycle_Nature,
|
||||||
RC_N: SettingGamepad.Button_Cycle_Variant,
|
RC_N: SettingGamepad.Button_Cycle_Tera,
|
||||||
START: SettingGamepad.Button_Menu,
|
START: SettingGamepad.Button_Menu,
|
||||||
SELECT: SettingGamepad.Button_Stats,
|
SELECT: SettingGamepad.Button_Stats,
|
||||||
LB: SettingGamepad.Button_Cycle_Form,
|
LB: SettingGamepad.Button_Cycle_Form,
|
||||||
|
1052
src/data/ability.ts
@ -708,6 +708,7 @@ export class FreshStartChallenge extends Challenge {
|
|||||||
pokemon.variant = 0; // Not shiny
|
pokemon.variant = 0; // Not shiny
|
||||||
pokemon.formIndex = 0; // Froakie should be base form
|
pokemon.formIndex = 0; // Froakie should be base form
|
||||||
pokemon.ivs = [ 15, 15, 15, 15, 15, 15 ]; // Default IVs of 15 for all stats (Updated to 15 from 10 in 1.2.0)
|
pokemon.ivs = [ 15, 15, 15, 15, 15, 15 ]; // Default IVs of 15 for all stats (Updated to 15 from 10 in 1.2.0)
|
||||||
|
pokemon.teraType = pokemon.species.type1; // Always primary tera type
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
711
src/data/move.ts
@ -1,5 +1,16 @@
|
|||||||
import { ChargeAnim, initMoveAnim, loadMoveAnimAssets, MoveChargeAnim } from "./battle-anims";
|
import { ChargeAnim, MoveChargeAnim } from "./battle-anims";
|
||||||
import { CommandedTag, EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, SubstituteTag, TrappedTag, TypeBoostTag } from "./battler-tags";
|
import {
|
||||||
|
CommandedTag,
|
||||||
|
EncoreTag,
|
||||||
|
GulpMissileTag,
|
||||||
|
HelpingHandTag,
|
||||||
|
SemiInvulnerableTag,
|
||||||
|
ShellTrapTag,
|
||||||
|
StockpilingTag,
|
||||||
|
SubstituteTag,
|
||||||
|
TrappedTag,
|
||||||
|
TypeBoostTag,
|
||||||
|
} from "./battler-tags";
|
||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import type { AttackMoveResult, TurnMove } from "../field/pokemon";
|
import type { AttackMoveResult, TurnMove } from "../field/pokemon";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
@ -30,7 +41,7 @@ import { Biome } from "#enums/biome";
|
|||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { MoveUsedEvent } from "#app/events/battle-scene";
|
import { MoveUsedEvent } from "#app/events/battle-scene";
|
||||||
import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat";
|
import { BATTLE_STATS, type BattleStat, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat";
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
@ -46,6 +57,10 @@ import { applyChallenges, ChallengeType } from "./challenge";
|
|||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase";
|
||||||
|
import { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase";
|
||||||
|
import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase";
|
||||||
|
import { MoveAnimPhase } from "#app/phases/move-anim-phase";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
@ -1057,7 +1072,7 @@ export abstract class MoveAttr {
|
|||||||
* @param args Set of unique arguments needed by this attribute
|
* @param args Set of unique arguments needed by this attribute
|
||||||
* @returns true if application of the ability succeeds
|
* @returns true if application of the ability succeeds
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1200,7 +1215,7 @@ export class MoveEffectAttr extends MoveAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Applies move effects so long as they are able based on {@linkcode canApply} */
|
/** Applies move effects so long as they are able based on {@linkcode canApply} */
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
||||||
return this.canApply(user, target, move, args);
|
return this.canApply(user, target, move, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1866,7 +1881,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
|||||||
* @param args - n/a
|
* @param args - n/a
|
||||||
* @returns A boolean indicating whether the effect was successfully applied.
|
* @returns A boolean indicating whether the effect was successfully applied.
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const targetAlly = target.getAlly();
|
const targetAlly = target.getAlly();
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
|
||||||
@ -2406,32 +2421,27 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
|
|||||||
this.chance = chance;
|
this.chance = chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return new Promise<boolean>(resolve => {
|
if (move.hitsSubstitute(user, target)) {
|
||||||
if (move.hitsSubstitute(user, target)) {
|
return false;
|
||||||
return resolve(false);
|
}
|
||||||
}
|
|
||||||
const rand = Phaser.Math.RND.realInRange(0, 1);
|
|
||||||
if (rand >= this.chance) {
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable);
|
|
||||||
if (heldItems.length) {
|
|
||||||
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
|
|
||||||
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
|
|
||||||
const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
|
||||||
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
|
|
||||||
globalScene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => {
|
|
||||||
if (success) {
|
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name }));
|
|
||||||
}
|
|
||||||
resolve(success);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(false);
|
const rand = Phaser.Math.RND.realInRange(0, 1);
|
||||||
});
|
if (rand >= this.chance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const heldItems = this.getTargetHeldItems(target).filter((i) => i.isTransferable);
|
||||||
|
if (heldItems.length) {
|
||||||
|
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
|
||||||
|
const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
|
||||||
|
const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier);
|
||||||
|
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
|
||||||
|
if (globalScene.tryTransferHeldItemModifier(stolenItem, user, false)) {
|
||||||
|
globalScene.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
|
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
|
||||||
@ -2875,9 +2885,7 @@ export class WeatherInstantChargeAttr extends InstantChargeAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class OverrideMoveEffectAttr extends MoveAttr {
|
export class OverrideMoveEffectAttr extends MoveAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
//const overridden = args[0] as Utils.BooleanHolder;
|
|
||||||
//const virtual = arg[1] as boolean;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2903,26 +2911,27 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
|||||||
this.chargeText = chargeText;
|
this.chargeText = chargeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
// Edge case for the move applied on a pokemon that has fainted
|
// Edge case for the move applied on a pokemon that has fainted
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return Promise.resolve(true);
|
return true;
|
||||||
}
|
}
|
||||||
const side = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
|
||||||
return new Promise(resolve => {
|
|
||||||
if (args.length < 2 || !args[1]) {
|
|
||||||
new MoveChargeAnim(this.chargeAnim, move.id, user).play(false, () => {
|
|
||||||
(args[0] as Utils.BooleanHolder).value = true;
|
|
||||||
globalScene.queueMessage(this.chargeText.replace("{TARGET}", getPokemonNameWithAffix(target)).replace("{USER}", getPokemonNameWithAffix(user)));
|
|
||||||
user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER });
|
|
||||||
globalScene.arena.addTag(this.tagType, 3, move.id, user.id, side, false, target.getBattlerIndex());
|
|
||||||
|
|
||||||
resolve(true);
|
const overridden = args[0] as Utils.BooleanHolder;
|
||||||
});
|
const virtual = args[1] as boolean;
|
||||||
} else {
|
|
||||||
globalScene.ui.showText(i18next.t("moveTriggers:tookMoveAttack", { pokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(target.id) ?? undefined), moveName: move.name }), null, () => resolve(true));
|
if (!virtual) {
|
||||||
}
|
overridden.value = true;
|
||||||
});
|
globalScene.unshiftPhase(new MoveAnimPhase(new MoveChargeAnim(this.chargeAnim, move.id, user)));
|
||||||
|
globalScene.queueMessage(this.chargeText.replace("{TARGET}", getPokemonNameWithAffix(target)).replace("{USER}", getPokemonNameWithAffix(user)));
|
||||||
|
user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER });
|
||||||
|
const side = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
globalScene.arena.addTag(this.tagType, 3, move.id, user.id, side, false, target.getBattlerIndex());
|
||||||
|
} else {
|
||||||
|
globalScene.queueMessage(i18next.t("moveTriggers:tookMoveAttack", { pokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(target.id) ?? undefined), moveName: move.name }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3053,7 +3062,7 @@ export class StatStageChangeAttr extends MoveEffectAttr {
|
|||||||
* @param args unused
|
* @param args unused
|
||||||
* @returns whether stat stages were changed
|
* @returns whether stat stages were changed
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
||||||
if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) {
|
if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3131,7 +3140,7 @@ export class SecretPowerAttr extends MoveEffectAttr {
|
|||||||
* Used to apply the secondary effect to the target Pokemon
|
* Used to apply the secondary effect to the target Pokemon
|
||||||
* @returns `true` if a secondary effect is successfully applied
|
* @returns `true` if a secondary effect is successfully applied
|
||||||
*/
|
*/
|
||||||
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
||||||
if (!super.apply(user, target, move, args)) {
|
if (!super.apply(user, target, move, args)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3286,8 +3295,8 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const randStats = BATTLE_STATS.filter(s => target.getStatStage(s) < 6);
|
const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6);
|
||||||
if (randStats.length > 0) {
|
if (randStats.length > 0) {
|
||||||
const boostStat = [ randStats[user.randSeedInt(randStats.length)] ];
|
const boostStat = [ randStats[user.randSeedInt(randStats.length)] ];
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2));
|
globalScene.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2));
|
||||||
@ -3324,17 +3333,14 @@ export class CutHpStatStageBoostAttr extends StatStageChangeAttr {
|
|||||||
this.messageCallback = messageCallback;
|
this.messageCallback = messageCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return new Promise<boolean>(resolve => {
|
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
|
||||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
|
user.updateInfo();
|
||||||
user.updateInfo().then(() => {
|
const ret = super.apply(user, target, move, args);
|
||||||
const ret = super.apply(user, target, move, args);
|
if (this.messageCallback) {
|
||||||
if (this.messageCallback) {
|
this.messageCallback(user);
|
||||||
this.messageCallback(user);
|
}
|
||||||
}
|
return ret;
|
||||||
resolve(ret);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
@ -3426,28 +3432,27 @@ export class ResetStatsAttr extends MoveEffectAttr {
|
|||||||
super();
|
super();
|
||||||
this.targetAllPokemon = targetAllPokemon;
|
this.targetAllPokemon = targetAllPokemon;
|
||||||
}
|
}
|
||||||
async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
|
||||||
const promises: Promise<void>[] = [];
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
if (this.targetAllPokemon) { // Target all pokemon on the field when Freezy Frost or Haze are used
|
if (this.targetAllPokemon) {
|
||||||
|
// Target all pokemon on the field when Freezy Frost or Haze are used
|
||||||
const activePokemon = globalScene.getField(true);
|
const activePokemon = globalScene.getField(true);
|
||||||
activePokemon.forEach(p => promises.push(this.resetStats(p)));
|
activePokemon.forEach((p) => this.resetStats(p));
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:statEliminated"));
|
globalScene.queueMessage(i18next.t("moveTriggers:statEliminated"));
|
||||||
} else { // Affects only the single target when Clear Smog is used
|
} else { // Affects only the single target when Clear Smog is used
|
||||||
if (!move.hitsSubstitute(user, target)) {
|
if (!move.hitsSubstitute(user, target)) {
|
||||||
promises.push(this.resetStats(target));
|
this.resetStats(target);
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) }));
|
globalScene.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetStats(pokemon: Pokemon): Promise<void> {
|
private resetStats(pokemon: Pokemon): void {
|
||||||
for (const s of BATTLE_STATS) {
|
for (const s of BATTLE_STATS) {
|
||||||
pokemon.setStatStage(s, 0);
|
pokemon.setStatStage(s, 0);
|
||||||
}
|
}
|
||||||
return pokemon.updateInfo();
|
pokemon.updateInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3503,43 +3508,28 @@ export class SwapStatStagesAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class HpSplitAttr extends MoveEffectAttr {
|
export class HpSplitAttr extends MoveEffectAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return new Promise(resolve => {
|
if (!super.apply(user, target, move, args)) {
|
||||||
if (!super.apply(user, target, move, args)) {
|
return false;
|
||||||
return resolve(false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const infoUpdates: Promise<void>[] = [];
|
const hpValue = Math.floor((target.hp + user.hp) / 2);
|
||||||
|
[ user, target ].forEach((p) => {
|
||||||
const hpValue = Math.floor((target.hp + user.hp) / 2);
|
if (p.hp < hpValue) {
|
||||||
if (user.hp < hpValue) {
|
const healing = p.heal(hpValue - p.hp);
|
||||||
const healing = user.heal(hpValue - user.hp);
|
|
||||||
if (healing) {
|
if (healing) {
|
||||||
globalScene.damageNumberHandler.add(user, healing, HitResult.HEAL);
|
globalScene.damageNumberHandler.add(p, healing, HitResult.HEAL);
|
||||||
}
|
}
|
||||||
} else if (user.hp > hpValue) {
|
} else if (p.hp > hpValue) {
|
||||||
const damage = user.damage(user.hp - hpValue, true);
|
const damage = p.damage(p.hp - hpValue, true);
|
||||||
if (damage) {
|
if (damage) {
|
||||||
globalScene.damageNumberHandler.add(user, damage);
|
globalScene.damageNumberHandler.add(p, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infoUpdates.push(user.updateInfo());
|
p.updateInfo();
|
||||||
|
|
||||||
if (target.hp < hpValue) {
|
|
||||||
const healing = target.heal(hpValue - target.hp);
|
|
||||||
if (healing) {
|
|
||||||
globalScene.damageNumberHandler.add(user, healing, HitResult.HEAL);
|
|
||||||
}
|
|
||||||
} else if (target.hp > hpValue) {
|
|
||||||
const damage = target.damage(target.hp - hpValue, true);
|
|
||||||
if (damage) {
|
|
||||||
globalScene.damageNumberHandler.add(target, damage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
infoUpdates.push(target.updateInfo());
|
|
||||||
|
|
||||||
return Promise.all(infoUpdates).then(() => resolve(true));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6024,44 +6014,44 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||||||
* @param args N/A
|
* @param args N/A
|
||||||
* @returns Promise, true if function succeeds.
|
* @returns Promise, true if function succeeds.
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return new Promise(resolve => {
|
// If user is player, checks if the user has fainted pokemon
|
||||||
// If user is player, checks if the user has fainted pokemon
|
if (user instanceof PlayerPokemon) {
|
||||||
if (user instanceof PlayerPokemon
|
globalScene.unshiftPhase(new RevivalBlessingPhase(user));
|
||||||
&& globalScene.getPlayerParty().findIndex(p => p.isFainted()) > -1) {
|
return true;
|
||||||
(user as PlayerPokemon).revivalBlessing().then(() => {
|
} else if (user instanceof EnemyPokemon && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) {
|
||||||
resolve(true);
|
// If used by an enemy trainer with at least one fainted non-boss Pokemon, this
|
||||||
});
|
// revives one of said Pokemon selected at random.
|
||||||
// If user is enemy, checks that it is a trainer, and it has fainted non-boss pokemon in party
|
const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss());
|
||||||
} else if (user instanceof EnemyPokemon
|
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)];
|
||||||
&& user.hasTrainer()
|
const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id);
|
||||||
&& globalScene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) {
|
pokemon.resetStatus();
|
||||||
// Selects a random fainted pokemon
|
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||||
const faintedPokemon = globalScene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss());
|
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true);
|
||||||
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)];
|
|
||||||
const slotIndex = globalScene.getEnemyParty().findIndex(p => pokemon.id === p.id);
|
|
||||||
pokemon.resetStatus();
|
|
||||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true);
|
|
||||||
|
|
||||||
if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1) {
|
if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1) {
|
||||||
const allyPokemon = user.getAlly();
|
const allyPokemon = user.getAlly();
|
||||||
if (slotIndex <= 1) {
|
if (slotIndex <= 1) {
|
||||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, false));
|
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, false));
|
||||||
} else if (allyPokemon.isFainted()) {
|
} else if (allyPokemon.isFainted()) {
|
||||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false));
|
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
resolve(true);
|
|
||||||
} else {
|
|
||||||
globalScene.queueMessage(i18next.t("battle:attackFailed"));
|
|
||||||
resolve(false);
|
|
||||||
}
|
}
|
||||||
});
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
getCondition(): MoveConditionFunc {
|
||||||
if (user.hasTrainer() && globalScene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) {
|
return (user, target, move) =>
|
||||||
|
(user instanceof PlayerPokemon && globalScene.getPlayerParty().some((p) => p.isFainted())) ||
|
||||||
|
(user instanceof EnemyPokemon &&
|
||||||
|
user.hasTrainer() &&
|
||||||
|
globalScene.getEnemyParty().some((p) => p.isFainted() && !p.isBoss()));
|
||||||
|
}
|
||||||
|
|
||||||
|
override getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number {
|
||||||
|
if (user.hasTrainer() && globalScene.getEnemyParty().some((p) => p.isFainted() && !p.isBoss())) {
|
||||||
return 20;
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6579,7 +6569,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr {
|
|||||||
class CallMoveAttr extends OverrideMoveEffectAttr {
|
class CallMoveAttr extends OverrideMoveEffectAttr {
|
||||||
protected invalidMoves: Moves[];
|
protected invalidMoves: Moves[];
|
||||||
protected hasTarget: boolean;
|
protected hasTarget: boolean;
|
||||||
async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const replaceMoveTarget = move.moveTarget === MoveTarget.NEAR_OTHER ? MoveTarget.NEAR_ENEMY : undefined;
|
const replaceMoveTarget = move.moveTarget === MoveTarget.NEAR_OTHER ? MoveTarget.NEAR_ENEMY : undefined;
|
||||||
const moveTargets = getMoveTargets(user, move.id, replaceMoveTarget);
|
const moveTargets = getMoveTargets(user, move.id, replaceMoveTarget);
|
||||||
if (moveTargets.targets.length === 0) {
|
if (moveTargets.targets.length === 0) {
|
||||||
@ -6589,11 +6579,8 @@ class CallMoveAttr extends OverrideMoveEffectAttr {
|
|||||||
? moveTargets.targets
|
? moveTargets.targets
|
||||||
: [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already
|
: [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already
|
||||||
user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true });
|
user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true });
|
||||||
|
globalScene.unshiftPhase(new LoadMoveAnimPhase(move.id));
|
||||||
globalScene.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true));
|
globalScene.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true));
|
||||||
|
|
||||||
await Promise.resolve(initMoveAnim(move.id).then(() => {
|
|
||||||
loadMoveAnimAssets([ move.id ], true);
|
|
||||||
}));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6626,7 +6613,7 @@ export class RandomMoveAttr extends CallMoveAttr {
|
|||||||
* @param move Move being used
|
* @param move Move being used
|
||||||
* @param args Unused
|
* @param args Unused
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const moveIds = Utils.getEnumValues(Moves).map(m => !this.invalidMoves.includes(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE);
|
const moveIds = Utils.getEnumValues(Moves).map(m => !this.invalidMoves.includes(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE);
|
||||||
let moveId: Moves = Moves.NONE;
|
let moveId: Moves = Moves.NONE;
|
||||||
do {
|
do {
|
||||||
@ -6663,7 +6650,7 @@ export class RandomMovesetMoveAttr extends CallMoveAttr {
|
|||||||
* @param move Move being used
|
* @param move Move being used
|
||||||
* @param args Unused
|
* @param args Unused
|
||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return super.apply(user, target, allMoves[this.moveId], args);
|
return super.apply(user, target, allMoves[this.moveId], args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6965,145 +6952,141 @@ const invalidCopycatMoves = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export class NaturePowerAttr extends OverrideMoveEffectAttr {
|
export class NaturePowerAttr extends OverrideMoveEffectAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return new Promise(resolve => {
|
let moveId;
|
||||||
let moveId;
|
switch (globalScene.arena.getTerrainType()) {
|
||||||
switch (globalScene.arena.getTerrainType()) {
|
// this allows terrains to 'override' the biome move
|
||||||
// this allows terrains to 'override' the biome move
|
case TerrainType.NONE:
|
||||||
case TerrainType.NONE:
|
switch (globalScene.arena.biomeType) {
|
||||||
switch (globalScene.arena.biomeType) {
|
case Biome.TOWN:
|
||||||
case Biome.TOWN:
|
moveId = Moves.ROUND;
|
||||||
moveId = Moves.ROUND;
|
break;
|
||||||
break;
|
case Biome.METROPOLIS:
|
||||||
case Biome.METROPOLIS:
|
moveId = Moves.TRI_ATTACK;
|
||||||
moveId = Moves.TRI_ATTACK;
|
break;
|
||||||
break;
|
case Biome.SLUM:
|
||||||
case Biome.SLUM:
|
moveId = Moves.SLUDGE_BOMB;
|
||||||
moveId = Moves.SLUDGE_BOMB;
|
break;
|
||||||
break;
|
case Biome.PLAINS:
|
||||||
case Biome.PLAINS:
|
moveId = Moves.SILVER_WIND;
|
||||||
moveId = Moves.SILVER_WIND;
|
break;
|
||||||
break;
|
case Biome.GRASS:
|
||||||
case Biome.GRASS:
|
moveId = Moves.GRASS_KNOT;
|
||||||
moveId = Moves.GRASS_KNOT;
|
break;
|
||||||
break;
|
case Biome.TALL_GRASS:
|
||||||
case Biome.TALL_GRASS:
|
moveId = Moves.POLLEN_PUFF;
|
||||||
moveId = Moves.POLLEN_PUFF;
|
break;
|
||||||
break;
|
case Biome.MEADOW:
|
||||||
case Biome.MEADOW:
|
moveId = Moves.GIGA_DRAIN;
|
||||||
moveId = Moves.GIGA_DRAIN;
|
break;
|
||||||
break;
|
case Biome.FOREST:
|
||||||
case Biome.FOREST:
|
moveId = Moves.BUG_BUZZ;
|
||||||
moveId = Moves.BUG_BUZZ;
|
break;
|
||||||
break;
|
case Biome.JUNGLE:
|
||||||
case Biome.JUNGLE:
|
moveId = Moves.LEAF_STORM;
|
||||||
moveId = Moves.LEAF_STORM;
|
break;
|
||||||
break;
|
case Biome.SEA:
|
||||||
case Biome.SEA:
|
moveId = Moves.HYDRO_PUMP;
|
||||||
moveId = Moves.HYDRO_PUMP;
|
break;
|
||||||
break;
|
case Biome.SWAMP:
|
||||||
case Biome.SWAMP:
|
moveId = Moves.MUD_BOMB;
|
||||||
moveId = Moves.MUD_BOMB;
|
break;
|
||||||
break;
|
case Biome.BEACH:
|
||||||
case Biome.BEACH:
|
moveId = Moves.SCALD;
|
||||||
moveId = Moves.SCALD;
|
break;
|
||||||
break;
|
case Biome.LAKE:
|
||||||
case Biome.LAKE:
|
moveId = Moves.BUBBLE_BEAM;
|
||||||
moveId = Moves.BUBBLE_BEAM;
|
break;
|
||||||
break;
|
case Biome.SEABED:
|
||||||
case Biome.SEABED:
|
moveId = Moves.BRINE;
|
||||||
moveId = Moves.BRINE;
|
break;
|
||||||
break;
|
case Biome.ISLAND:
|
||||||
case Biome.ISLAND:
|
moveId = Moves.LEAF_TORNADO;
|
||||||
moveId = Moves.LEAF_TORNADO;
|
break;
|
||||||
break;
|
case Biome.MOUNTAIN:
|
||||||
case Biome.MOUNTAIN:
|
moveId = Moves.AIR_SLASH;
|
||||||
moveId = Moves.AIR_SLASH;
|
break;
|
||||||
break;
|
case Biome.BADLANDS:
|
||||||
case Biome.BADLANDS:
|
moveId = Moves.EARTH_POWER;
|
||||||
moveId = Moves.EARTH_POWER;
|
break;
|
||||||
break;
|
case Biome.DESERT:
|
||||||
case Biome.DESERT:
|
moveId = Moves.SCORCHING_SANDS;
|
||||||
moveId = Moves.SCORCHING_SANDS;
|
break;
|
||||||
break;
|
case Biome.WASTELAND:
|
||||||
case Biome.WASTELAND:
|
moveId = Moves.DRAGON_PULSE;
|
||||||
moveId = Moves.DRAGON_PULSE;
|
break;
|
||||||
break;
|
case Biome.CONSTRUCTION_SITE:
|
||||||
case Biome.CONSTRUCTION_SITE:
|
moveId = Moves.STEEL_BEAM;
|
||||||
moveId = Moves.STEEL_BEAM;
|
break;
|
||||||
break;
|
case Biome.CAVE:
|
||||||
case Biome.CAVE:
|
moveId = Moves.POWER_GEM;
|
||||||
moveId = Moves.POWER_GEM;
|
break;
|
||||||
break;
|
case Biome.ICE_CAVE:
|
||||||
case Biome.ICE_CAVE:
|
moveId = Moves.ICE_BEAM;
|
||||||
moveId = Moves.ICE_BEAM;
|
break;
|
||||||
break;
|
case Biome.SNOWY_FOREST:
|
||||||
case Biome.SNOWY_FOREST:
|
moveId = Moves.FROST_BREATH;
|
||||||
moveId = Moves.FROST_BREATH;
|
break;
|
||||||
break;
|
case Biome.VOLCANO:
|
||||||
case Biome.VOLCANO:
|
moveId = Moves.LAVA_PLUME;
|
||||||
moveId = Moves.LAVA_PLUME;
|
break;
|
||||||
break;
|
case Biome.GRAVEYARD:
|
||||||
case Biome.GRAVEYARD:
|
moveId = Moves.SHADOW_BALL;
|
||||||
moveId = Moves.SHADOW_BALL;
|
break;
|
||||||
break;
|
case Biome.RUINS:
|
||||||
case Biome.RUINS:
|
moveId = Moves.ANCIENT_POWER;
|
||||||
moveId = Moves.ANCIENT_POWER;
|
break;
|
||||||
break;
|
case Biome.TEMPLE:
|
||||||
case Biome.TEMPLE:
|
moveId = Moves.EXTRASENSORY;
|
||||||
moveId = Moves.EXTRASENSORY;
|
break;
|
||||||
break;
|
case Biome.DOJO:
|
||||||
case Biome.DOJO:
|
moveId = Moves.FOCUS_BLAST;
|
||||||
moveId = Moves.FOCUS_BLAST;
|
break;
|
||||||
break;
|
case Biome.FAIRY_CAVE:
|
||||||
case Biome.FAIRY_CAVE:
|
moveId = Moves.ALLURING_VOICE;
|
||||||
moveId = Moves.ALLURING_VOICE;
|
break;
|
||||||
break;
|
case Biome.ABYSS:
|
||||||
case Biome.ABYSS:
|
moveId = Moves.OMINOUS_WIND;
|
||||||
moveId = Moves.OMINOUS_WIND;
|
break;
|
||||||
break;
|
case Biome.SPACE:
|
||||||
case Biome.SPACE:
|
moveId = Moves.DRACO_METEOR;
|
||||||
moveId = Moves.DRACO_METEOR;
|
break;
|
||||||
break;
|
case Biome.FACTORY:
|
||||||
case Biome.FACTORY:
|
moveId = Moves.FLASH_CANNON;
|
||||||
moveId = Moves.FLASH_CANNON;
|
break;
|
||||||
break;
|
case Biome.LABORATORY:
|
||||||
case Biome.LABORATORY:
|
moveId = Moves.ZAP_CANNON;
|
||||||
moveId = Moves.ZAP_CANNON;
|
break;
|
||||||
break;
|
case Biome.POWER_PLANT:
|
||||||
case Biome.POWER_PLANT:
|
moveId = Moves.CHARGE_BEAM;
|
||||||
moveId = Moves.CHARGE_BEAM;
|
break;
|
||||||
break;
|
case Biome.END:
|
||||||
case Biome.END:
|
moveId = Moves.ETERNABEAM;
|
||||||
moveId = Moves.ETERNABEAM;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case TerrainType.MISTY:
|
||||||
case TerrainType.MISTY:
|
moveId = Moves.MOONBLAST;
|
||||||
moveId = Moves.MOONBLAST;
|
break;
|
||||||
break;
|
case TerrainType.ELECTRIC:
|
||||||
case TerrainType.ELECTRIC:
|
moveId = Moves.THUNDERBOLT;
|
||||||
moveId = Moves.THUNDERBOLT;
|
break;
|
||||||
break;
|
case TerrainType.GRASSY:
|
||||||
case TerrainType.GRASSY:
|
moveId = Moves.ENERGY_BALL;
|
||||||
moveId = Moves.ENERGY_BALL;
|
break;
|
||||||
break;
|
case TerrainType.PSYCHIC:
|
||||||
case TerrainType.PSYCHIC:
|
moveId = Moves.PSYCHIC;
|
||||||
moveId = Moves.PSYCHIC;
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
// Just in case there's no match
|
||||||
// Just in case there's no match
|
moveId = Moves.TRI_ATTACK;
|
||||||
moveId = Moves.TRI_ATTACK;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
user.getMoveQueue().push({ move: moveId, targets: [ target.getBattlerIndex() ], ignorePP: true });
|
user.getMoveQueue().push({ move: moveId, targets: [ target.getBattlerIndex() ], ignorePP: true });
|
||||||
globalScene.unshiftPhase(new MovePhase(user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true));
|
globalScene.unshiftPhase(new LoadMoveAnimPhase(moveId));
|
||||||
initMoveAnim(moveId).then(() => {
|
globalScene.unshiftPhase(new MovePhase(user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true));
|
||||||
loadMoveAnimAssets([ moveId ], true)
|
return true;
|
||||||
.then(() => resolve(true));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7121,7 +7104,7 @@ export class CopyMoveAttr extends CallMoveAttr {
|
|||||||
this.invalidMoves = invalidMoves;
|
this.invalidMoves = invalidMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
this.hasTarget = this.mirrorMove;
|
this.hasTarget = this.mirrorMove;
|
||||||
const lastMove = this.mirrorMove ? target.getLastXMoves()[0].move : globalScene.currentBattle.lastMove;
|
const lastMove = this.mirrorMove ? target.getLastXMoves()[0].move : globalScene.currentBattle.lastMove;
|
||||||
return super.apply(user, target, allMoves[lastMove], args);
|
return super.apply(user, target, allMoves[lastMove], args);
|
||||||
@ -7682,50 +7665,15 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr {
|
|||||||
* Used by Transform
|
* Used by Transform
|
||||||
*/
|
*/
|
||||||
export class TransformAttr extends MoveEffectAttr {
|
export class TransformAttr extends MoveEffectAttr {
|
||||||
async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
if (!super.apply(user, target, move, args)) {
|
if (!super.apply(user, target, move, args)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises: Promise<void>[] = [];
|
globalScene.unshiftPhase(new PokemonTransformPhase(user.getBattlerIndex(), target.getBattlerIndex()));
|
||||||
user.summonData.speciesForm = target.getSpeciesForm();
|
|
||||||
user.summonData.gender = target.getGender();
|
|
||||||
|
|
||||||
// Power Trick's effect will not preserved after using Transform
|
|
||||||
user.removeTag(BattlerTagType.POWER_TRICK);
|
|
||||||
|
|
||||||
// Copy all stats (except HP)
|
|
||||||
for (const s of EFFECTIVE_STATS) {
|
|
||||||
user.setStat(s, target.getStat(s, false), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy all stat stages
|
|
||||||
for (const s of BATTLE_STATS) {
|
|
||||||
user.setStatStage(s, target.getStatStage(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
user.summonData.moveset = target.getMoveset().map((m) => {
|
|
||||||
if (m) {
|
|
||||||
// If PP value is less than 5, do nothing. If greater, we need to reduce the value to 5.
|
|
||||||
return new PokemonMove(m.moveId, 0, 0, false, Math.min(m.getMove().pp, 5));
|
|
||||||
} else {
|
|
||||||
console.warn(`Transform: somehow iterating over a ${m} value when copying moveset!`);
|
|
||||||
return new PokemonMove(Moves.NONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
user.summonData.types = target.getTypes();
|
|
||||||
promises.push(user.updateInfo());
|
|
||||||
|
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }));
|
globalScene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }));
|
||||||
|
|
||||||
promises.push(user.loadAssets(false).then(() => {
|
|
||||||
user.playAnim();
|
|
||||||
user.updateInfo();
|
|
||||||
// If the new ability activates immediately, it needs to happen after all the transform animations
|
|
||||||
user.setTempAbility(target.getAbility());
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8035,6 +7983,56 @@ export class AfterYouAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move effect to force the target to move last, ignoring priority.
|
||||||
|
* If applied to multiple targets, they move in speed order after all other moves.
|
||||||
|
* @extends MoveEffectAttr
|
||||||
|
*/
|
||||||
|
export class ForceLastAttr extends MoveEffectAttr {
|
||||||
|
/**
|
||||||
|
* Forces the target of this move to move last.
|
||||||
|
*
|
||||||
|
* @param user {@linkcode Pokemon} that is using the move.
|
||||||
|
* @param target {@linkcode Pokemon} that will be forced to move last.
|
||||||
|
* @param move {@linkcode Move} {@linkcode Moves.QUASH}
|
||||||
|
* @param _args N/A
|
||||||
|
* @returns true
|
||||||
|
*/
|
||||||
|
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
||||||
|
globalScene.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
||||||
|
|
||||||
|
const targetMovePhase = globalScene.findPhase<MovePhase>((phase) => phase.pokemon === target);
|
||||||
|
if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) {
|
||||||
|
// Finding the phase to insert the move in front of -
|
||||||
|
// Either the end of the turn or in front of another, slower move which has also been forced last
|
||||||
|
const prependPhase = globalScene.findPhase((phase) =>
|
||||||
|
[ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls))
|
||||||
|
|| (phase instanceof MovePhase) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM))
|
||||||
|
);
|
||||||
|
if (prependPhase) {
|
||||||
|
globalScene.phaseQueue.splice(
|
||||||
|
globalScene.phaseQueue.indexOf(prependPhase),
|
||||||
|
0,
|
||||||
|
new MovePhase(target, [ ...targetMovePhase.targets ], targetMovePhase.move, false, false, false, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether a {@linkcode MovePhase} has been forced last and the corresponding pokemon is slower than {@linkcode target} */
|
||||||
|
const phaseForcedSlower = (phase: MovePhase, target: Pokemon, trickRoom: boolean): boolean => {
|
||||||
|
let slower: boolean;
|
||||||
|
// quashed pokemon still have speed ties
|
||||||
|
if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) {
|
||||||
|
slower = !!target.randSeedInt(2);
|
||||||
|
} else {
|
||||||
|
slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD);
|
||||||
|
}
|
||||||
|
return phase.isForcedLast() && slower;
|
||||||
|
};
|
||||||
|
|
||||||
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !globalScene.arena.getTag(ArenaTagType.GRAVITY);
|
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !globalScene.arena.getTag(ArenaTagType.GRAVITY);
|
||||||
|
|
||||||
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
|
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
|
||||||
@ -8078,44 +8076,54 @@ const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) =
|
|||||||
|
|
||||||
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
||||||
|
|
||||||
function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise<void> {
|
function applyMoveAttrsInternal(
|
||||||
return new Promise(resolve => {
|
attrFilter: MoveAttrFilter,
|
||||||
const attrPromises: Promise<boolean>[] = [];
|
user: Pokemon | null,
|
||||||
const moveAttrs = move.attrs.filter(a => attrFilter(a));
|
target: Pokemon | null,
|
||||||
for (const attr of moveAttrs) {
|
move: Move,
|
||||||
const result = attr.apply(user, target, move, args);
|
args: any[],
|
||||||
if (result instanceof Promise) {
|
): void {
|
||||||
attrPromises.push(result);
|
move.attrs.filter((attr) => attrFilter(attr)).forEach((attr) => attr.apply(user, target, move, args));
|
||||||
}
|
|
||||||
}
|
|
||||||
Promise.allSettled(attrPromises).then(() => resolve());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMoveChargeAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: ChargingMove, args: any[]): Promise<void> {
|
function applyMoveChargeAttrsInternal(
|
||||||
return new Promise(resolve => {
|
attrFilter: MoveAttrFilter,
|
||||||
const chargeAttrPromises: Promise<boolean>[] = [];
|
user: Pokemon | null,
|
||||||
const chargeMoveAttrs = move.chargeAttrs.filter(a => attrFilter(a));
|
target: Pokemon | null,
|
||||||
for (const attr of chargeMoveAttrs) {
|
move: ChargingMove,
|
||||||
const result = attr.apply(user, target, move, args);
|
args: any[],
|
||||||
if (result instanceof Promise) {
|
): void {
|
||||||
chargeAttrPromises.push(result);
|
move.chargeAttrs.filter((attr) => attrFilter(attr)).forEach((attr) => attr.apply(user, target, move, args));
|
||||||
}
|
|
||||||
}
|
|
||||||
Promise.allSettled(chargeAttrPromises).then(() => resolve());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyMoveAttrs(attrType: Constructor<MoveAttr>, user: Pokemon | null, target: Pokemon | null, move: Move, ...args: any[]): Promise<void> {
|
export function applyMoveAttrs(
|
||||||
return applyMoveAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args);
|
attrType: Constructor<MoveAttr>,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon | null, move: Move, ...args: any[]): Promise<void> {
|
export function applyFilteredMoveAttrs(
|
||||||
return applyMoveAttrsInternal(attrFilter, user, target, move, args);
|
attrFilter: MoveAttrFilter,
|
||||||
|
user: Pokemon,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveAttrsInternal(attrFilter, user, target, move, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyMoveChargeAttrs(attrType: Constructor<MoveAttr>, user: Pokemon | null, target: Pokemon | null, move: ChargingMove, ...args: any[]): Promise<void> {
|
export function applyMoveChargeAttrs(
|
||||||
return applyMoveChargeAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args);
|
attrType: Constructor<MoveAttr>,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: ChargingMove,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveChargeAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MoveCondition {
|
export class MoveCondition {
|
||||||
@ -9914,7 +9922,8 @@ export function initMoves() {
|
|||||||
.attr(RemoveHeldItemAttr, true),
|
.attr(RemoveHeldItemAttr, true),
|
||||||
new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5)
|
new StatusMove(Moves.QUASH, Type.DARK, 100, 15, -1, 0, 5)
|
||||||
.condition(failIfSingleBattle)
|
.condition(failIfSingleBattle)
|
||||||
.unimplemented(),
|
.condition((user, target, move) => !target.turnData.acted)
|
||||||
|
.attr(ForceLastAttr),
|
||||||
new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
|
new AttackMove(Moves.ACROBATICS, Type.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))),
|
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))),
|
||||||
new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5)
|
new StatusMove(Moves.REFLECT_TYPE, Type.NORMAL, -1, 15, -1, 0, 5)
|
||||||
|
@ -151,7 +151,7 @@ async function spawnNextTrainerOrEndEncounter() {
|
|||||||
|
|
||||||
// Give 10x Voucher
|
// Give 10x Voucher
|
||||||
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
|
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
|
||||||
await globalScene.addModifier(newModifier);
|
globalScene.addModifier(newModifier);
|
||||||
globalScene.playSound("item_fanfare");
|
globalScene.playSound("item_fanfare");
|
||||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
|
|||||||
// Copy old items to new pokemon
|
// Copy old items to new pokemon
|
||||||
for (const item of transformation.heldItems) {
|
for (const item of transformation.heldItems) {
|
||||||
item.pokemonId = newPokemon.id;
|
item.pokemonId = newPokemon.id;
|
||||||
await globalScene.addModifier(item, false, false, false, true);
|
globalScene.addModifier(item, false, false, false, true);
|
||||||
}
|
}
|
||||||
// Any pokemon that is below 570 BST gets +20 permanent BST to 3 stats
|
// Any pokemon that is below 570 BST gets +20 permanent BST to 3 stats
|
||||||
if (shouldGetOldGateau(newPokemon)) {
|
if (shouldGetOldGateau(newPokemon)) {
|
||||||
@ -416,7 +416,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
|
|||||||
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
||||||
const modifier = modType?.newModifier(newPokemon);
|
const modifier = modType?.newModifier(newPokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
await globalScene.addModifier(modifier, false, false, false, true);
|
globalScene.addModifier(modifier, false, false, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: numb
|
|||||||
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE);
|
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE);
|
||||||
const modifier = modType?.newModifier(pokemon);
|
const modifier = modType?.newModifier(pokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
await globalScene.addModifier(modifier, false, false, false, true);
|
globalScene.addModifier(modifier, false, false, false, true);
|
||||||
pokemon.calculateStats();
|
pokemon.calculateStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ export async function applyModifierTypeToPlayerPokemon(pokemon: PlayerPokemon, m
|
|||||||
return applyModifierTypeToPlayerPokemon(pokemon, fallbackModifierType);
|
return applyModifierTypeToPlayerPokemon(pokemon, fallbackModifierType);
|
||||||
}
|
}
|
||||||
|
|
||||||
await globalScene.addModifier(modifier, false, false, false, true);
|
globalScene.addModifier(modifier, false, false, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1850,7 +1850,7 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(Species.REGIGIGAS, 4, true, false, false, "Colossal Pokémon", Type.NORMAL, null, 3.7, 420, Abilities.SLOW_START, Abilities.NONE, Abilities.NORMALIZE, 670, 110, 160, 110, 80, 110, 100, 3, 0, 335, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(Species.REGIGIGAS, 4, true, false, false, "Colossal Pokémon", Type.NORMAL, null, 3.7, 420, Abilities.SLOW_START, Abilities.NONE, Abilities.NORMALIZE, 670, 110, 160, 110, 80, 110, 100, 3, 0, 335, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(Species.GIRATINA, 4, false, true, false, "Renegade Pokémon", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340, GrowthRate.SLOW, null, false, true,
|
new PokemonSpecies(Species.GIRATINA, 4, false, true, false, "Renegade Pokémon", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340, GrowthRate.SLOW, null, false, true,
|
||||||
new PokemonForm("Altered Forme", "altered", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340, false, null, true),
|
new PokemonForm("Altered Forme", "altered", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340, false, null, true),
|
||||||
new PokemonForm("Origin Forme", "origin", Type.GHOST, Type.DRAGON, 6.9, 650, Abilities.LEVITATE, Abilities.NONE, Abilities.NONE, 680, 150, 120, 100, 120, 100, 90, 3, 0, 340),
|
new PokemonForm("Origin Forme", "origin", Type.GHOST, Type.DRAGON, 6.9, 650, Abilities.LEVITATE, Abilities.NONE, Abilities.LEVITATE, 680, 150, 120, 100, 120, 100, 90, 3, 0, 340),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.CRESSELIA, 4, true, false, false, "Lunar Pokémon", Type.PSYCHIC, null, 1.5, 85.6, Abilities.LEVITATE, Abilities.NONE, Abilities.NONE, 580, 120, 70, 110, 75, 120, 85, 3, 100, 300, GrowthRate.SLOW, 0, false),
|
new PokemonSpecies(Species.CRESSELIA, 4, true, false, false, "Lunar Pokémon", Type.PSYCHIC, null, 1.5, 85.6, Abilities.LEVITATE, Abilities.NONE, Abilities.NONE, 580, 120, 70, 110, 75, 120, 85, 3, 100, 300, GrowthRate.SLOW, 0, false),
|
||||||
new PokemonSpecies(Species.PHIONE, 4, false, false, true, "Sea Drifter Pokémon", Type.WATER, null, 0.4, 3.1, Abilities.HYDRATION, Abilities.NONE, Abilities.NONE, 480, 80, 80, 80, 80, 80, 80, 30, 70, 240, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(Species.PHIONE, 4, false, false, true, "Sea Drifter Pokémon", Type.WATER, null, 0.4, 3.1, Abilities.HYDRATION, Abilities.NONE, Abilities.NONE, 480, 80, 80, 80, 80, 80, 80, 30, 70, 240, GrowthRate.SLOW, null, false),
|
||||||
|
@ -13,7 +13,7 @@ export enum Button {
|
|||||||
CYCLE_GENDER,
|
CYCLE_GENDER,
|
||||||
CYCLE_ABILITY,
|
CYCLE_ABILITY,
|
||||||
CYCLE_NATURE,
|
CYCLE_NATURE,
|
||||||
V,
|
CYCLE_TERA,
|
||||||
SPEED_UP,
|
SPEED_UP,
|
||||||
SLOW_DOWN
|
SLOW_DOWN,
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,6 @@ import { MoveEndPhase } from "#app/phases/move-end-phase";
|
|||||||
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
import { PokemonAnimType } from "#enums/pokemon-anim-type";
|
import { PokemonAnimType } from "#enums/pokemon-anim-type";
|
||||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
@ -2348,12 +2347,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const maxPower = Math.min(movePool.reduce((v, m) => Math.max(allMoves[m[0]].power, v), 40), 90);
|
const maxPower = Math.min(movePool.reduce((v, m) => Math.max(allMoves[m[0]].power, v), 40), 90);
|
||||||
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === MoveCategory.STATUS ? 1 : Math.max(Math.min(allMoves[m[0]].power / maxPower, 1), 0.5)) ]);
|
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === MoveCategory.STATUS ? 1 : Math.max(Math.min(allMoves[m[0]].power / maxPower, 1), 0.5)) ]);
|
||||||
|
|
||||||
// Weight damaging moves against the lower stat
|
// Weight damaging moves against the lower stat. This uses a non-linear relationship.
|
||||||
|
// If the higher stat is 1 - 1.09x higher, no change. At higher stat ~1.38x lower stat, off-stat moves have half weight.
|
||||||
|
// One third weight at ~1.58x higher, one quarter weight at ~1.73x higher, one fifth at ~1.87x, and one tenth at ~2.35x higher.
|
||||||
const atk = this.getStat(Stat.ATK);
|
const atk = this.getStat(Stat.ATK);
|
||||||
const spAtk = this.getStat(Stat.SPATK);
|
const spAtk = this.getStat(Stat.SPATK);
|
||||||
const worseCategory: MoveCategory = atk > spAtk ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL;
|
const worseCategory: MoveCategory = atk > spAtk ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL;
|
||||||
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
|
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
|
||||||
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]);
|
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? Math.min(Math.pow(statRatio, 3) * 1.3, 1) : 1) ]);
|
||||||
|
|
||||||
/** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */
|
/** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */
|
||||||
let weightMultiplier = 0.9;
|
let weightMultiplier = 0.9;
|
||||||
@ -4509,43 +4510,6 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
this.friendship = Math.max(this.friendship + friendship, 0);
|
this.friendship = Math.max(this.friendship + friendship, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Handles Revival Blessing when used by player.
|
|
||||||
* @returns Promise to revive a pokemon.
|
|
||||||
* @see {@linkcode RevivalBlessingAttr}
|
|
||||||
*/
|
|
||||||
revivalBlessing(): Promise<void> {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
globalScene.ui.setMode(Mode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.getFieldIndex(), (slotIndex:number, option: PartyOption) => {
|
|
||||||
if (slotIndex >= 0 && slotIndex < 6) {
|
|
||||||
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
|
||||||
if (!pokemon || !pokemon.isFainted()) {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
pokemon.resetTurnData();
|
|
||||||
pokemon.resetStatus();
|
|
||||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name }), 0, true);
|
|
||||||
|
|
||||||
if (globalScene.currentBattle.double && globalScene.getPlayerParty().length > 1) {
|
|
||||||
const allyPokemon = this.getAlly();
|
|
||||||
if (slotIndex <= 1) {
|
|
||||||
// Revived ally pokemon
|
|
||||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, true));
|
|
||||||
globalScene.unshiftPhase(new ToggleDoublePositionPhase(true));
|
|
||||||
} else if (allyPokemon.isFainted()) {
|
|
||||||
// Revived party pokemon, and ally pokemon is fainted
|
|
||||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, true));
|
|
||||||
globalScene.unshiftPhase(new ToggleDoublePositionPhase(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
globalScene.ui.setMode(Mode.MESSAGE).then(() => resolve());
|
|
||||||
}, PartyUiHandler.FilterFainted);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getPossibleEvolution(evolution: SpeciesFormEvolution | null): Promise<Pokemon> {
|
getPossibleEvolution(evolution: SpeciesFormEvolution | null): Promise<Pokemon> {
|
||||||
if (!evolution) {
|
if (!evolution) {
|
||||||
@ -4727,70 +4691,62 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Promise to fuse two PlayerPokemon together
|
* Returns a Promise to fuse two PlayerPokemon together
|
||||||
* @param pokemon The PlayerPokemon to fuse to this one
|
* @param pokemon The PlayerPokemon to fuse to this one
|
||||||
*/
|
*/
|
||||||
fuse(pokemon: PlayerPokemon): Promise<void> {
|
fuse(pokemon: PlayerPokemon): void {
|
||||||
return new Promise(resolve => {
|
this.fusionSpecies = pokemon.species;
|
||||||
this.fusionSpecies = pokemon.species;
|
this.fusionFormIndex = pokemon.formIndex;
|
||||||
this.fusionFormIndex = pokemon.formIndex;
|
this.fusionAbilityIndex = pokemon.abilityIndex;
|
||||||
this.fusionAbilityIndex = pokemon.abilityIndex;
|
this.fusionShiny = pokemon.shiny;
|
||||||
this.fusionShiny = pokemon.shiny;
|
this.fusionVariant = pokemon.variant;
|
||||||
this.fusionVariant = pokemon.variant;
|
this.fusionGender = pokemon.gender;
|
||||||
this.fusionGender = pokemon.gender;
|
this.fusionLuck = pokemon.luck;
|
||||||
this.fusionLuck = pokemon.luck;
|
this.fusionCustomPokemonData = pokemon.customPokemonData;
|
||||||
this.fusionCustomPokemonData = pokemon.customPokemonData;
|
if (pokemon.pauseEvolutions || this.pauseEvolutions) {
|
||||||
if ((pokemon.pauseEvolutions) || (this.pauseEvolutions)) {
|
this.pauseEvolutions = true;
|
||||||
this.pauseEvolutions = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
globalScene.validateAchv(achvs.SPLICE);
|
globalScene.validateAchv(achvs.SPLICE);
|
||||||
globalScene.gameData.gameStats.pokemonFused++;
|
globalScene.gameData.gameStats.pokemonFused++;
|
||||||
|
|
||||||
// Store the average HP% that each Pokemon has
|
// Store the average HP% that each Pokemon has
|
||||||
const maxHp = this.getMaxHp();
|
const maxHp = this.getMaxHp();
|
||||||
const newHpPercent = ((pokemon.hp / pokemon.getMaxHp()) + (this.hp / maxHp)) / 2;
|
const newHpPercent = (pokemon.hp / pokemon.getMaxHp() + this.hp / maxHp) / 2;
|
||||||
|
|
||||||
this.generateName();
|
this.generateName();
|
||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
|
|
||||||
// Set this Pokemon's HP to the average % of both fusion components
|
// Set this Pokemon's HP to the average % of both fusion components
|
||||||
this.hp = Math.round(maxHp * newHpPercent);
|
this.hp = Math.round(maxHp * newHpPercent);
|
||||||
if (!this.isFainted()) {
|
if (!this.isFainted()) {
|
||||||
// If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum
|
// If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum
|
||||||
this.hp = Math.min(this.hp, maxHp);
|
this.hp = Math.min(this.hp, maxHp);
|
||||||
this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two
|
this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two
|
||||||
} else if (!pokemon.isFainted()) {
|
} else if (!pokemon.isFainted()) {
|
||||||
// If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero
|
// If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero
|
||||||
this.hp = Math.max(this.hp, 1);
|
this.hp = Math.max(this.hp, 1);
|
||||||
this.status = pokemon.status; // Inherit the other Pokemon's status
|
this.status = pokemon.status; // Inherit the other Pokemon's status
|
||||||
}
|
}
|
||||||
|
|
||||||
this.generateCompatibleTms();
|
this.generateCompatibleTms();
|
||||||
this.updateInfo(true);
|
this.updateInfo(true);
|
||||||
const fusedPartyMemberIndex = globalScene.getPlayerParty().indexOf(pokemon);
|
const fusedPartyMemberIndex = globalScene.getPlayerParty().indexOf(pokemon);
|
||||||
let partyMemberIndex = globalScene.getPlayerParty().indexOf(this);
|
let partyMemberIndex = globalScene.getPlayerParty().indexOf(this);
|
||||||
if (partyMemberIndex > fusedPartyMemberIndex) {
|
if (partyMemberIndex > fusedPartyMemberIndex) {
|
||||||
partyMemberIndex--;
|
partyMemberIndex--;
|
||||||
}
|
}
|
||||||
const fusedPartyMemberHeldModifiers = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
const fusedPartyMemberHeldModifiers = globalScene.findModifiers((m) => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||||
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
for (const modifier of fusedPartyMemberHeldModifiers) {
|
||||||
const transferModifiers: Promise<boolean>[] = [];
|
globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false);
|
||||||
for (const modifier of fusedPartyMemberHeldModifiers) {
|
}
|
||||||
transferModifiers.push(globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false));
|
globalScene.updateModifiers(true, true);
|
||||||
}
|
globalScene.removePartyMemberModifiers(fusedPartyMemberIndex);
|
||||||
Promise.allSettled(transferModifiers).then(() => {
|
globalScene.getPlayerParty().splice(fusedPartyMemberIndex, 1)[0];
|
||||||
globalScene.updateModifiers(true, true).then(() => {
|
const newPartyMemberIndex = globalScene.getPlayerParty().indexOf(this);
|
||||||
globalScene.removePartyMemberModifiers(fusedPartyMemberIndex);
|
pokemon.getMoveset(true).map((m: PokemonMove) => globalScene.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id)));
|
||||||
globalScene.getPlayerParty().splice(fusedPartyMemberIndex, 1)[0];
|
pokemon.destroy();
|
||||||
const newPartyMemberIndex = globalScene.getPlayerParty().indexOf(this);
|
this.updateFusionPalette();
|
||||||
pokemon.getMoveset(true).map((m: PokemonMove) => globalScene.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id)));
|
|
||||||
pokemon.destroy();
|
|
||||||
this.updateFusionPalette();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unfuse(): Promise<void> {
|
unfuse(): Promise<void> {
|
||||||
|
@ -1822,7 +1822,7 @@ const modifierPool: ModifierPool = {
|
|||||||
|
|
||||||
if (!isHoldingOrb) {
|
if (!isHoldingOrb) {
|
||||||
const moveset = p.getMoveset(true).filter(m => !isNullOrUndefined(m)).map(m => m.moveId);
|
const moveset = p.getMoveset(true).filter(m => !isNullOrUndefined(m)).map(m => m.moveId);
|
||||||
const canSetStatus = p.canSetStatus(StatusEffect.TOXIC, true, true, null, true);
|
const canSetStatus = p.canSetStatus(StatusEffect.BURN, true, true, null, true);
|
||||||
|
|
||||||
// Moves that take advantage of obtaining the actual status effect
|
// Moves that take advantage of obtaining the actual status effect
|
||||||
const hasStatusMoves = [ Moves.FACADE, Moves.PSYCHO_SHIFT ]
|
const hasStatusMoves = [ Moves.FACADE, Moves.PSYCHO_SHIFT ]
|
||||||
|
@ -158,7 +158,7 @@ export abstract class Modifier {
|
|||||||
* Handles applying of {@linkcode Modifier}
|
* Handles applying of {@linkcode Modifier}
|
||||||
* @param args collection of all passed parameters
|
* @param args collection of all passed parameters
|
||||||
*/
|
*/
|
||||||
abstract apply(...args: unknown[]): boolean | Promise<boolean>;
|
abstract apply(...args: unknown[]): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class PersistentModifier extends Modifier {
|
export abstract class PersistentModifier extends Modifier {
|
||||||
@ -1949,7 +1949,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier {
|
|||||||
* @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item
|
* @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item
|
||||||
* @param args Additional arguments passed to {@linkcode ConsumablePokemonModifier.apply}
|
* @param args Additional arguments passed to {@linkcode ConsumablePokemonModifier.apply}
|
||||||
*/
|
*/
|
||||||
abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean | Promise<boolean>;
|
abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean;
|
||||||
|
|
||||||
getPokemon() {
|
getPokemon() {
|
||||||
return globalScene.getPlayerParty().find(p => p.id === this.pokemonId);
|
return globalScene.getPlayerParty().find(p => p.id === this.pokemonId);
|
||||||
@ -2288,8 +2288,8 @@ export class FusePokemonModifier extends ConsumablePokemonModifier {
|
|||||||
* @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon}
|
* @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon}
|
||||||
* @returns always Promise<true>
|
* @returns always Promise<true>
|
||||||
*/
|
*/
|
||||||
override async apply(playerPokemon: PlayerPokemon, playerPokemon2: PlayerPokemon): Promise<boolean> {
|
override apply(playerPokemon: PlayerPokemon, playerPokemon2: PlayerPokemon): boolean {
|
||||||
await playerPokemon.fuse(playerPokemon2);
|
playerPokemon.fuse(playerPokemon2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3136,8 +3136,6 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
|||||||
let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct?
|
let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct?
|
||||||
let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
||||||
|
|
||||||
const heldItemTransferPromises: Promise<void>[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < transferredItemCount; i++) {
|
for (let i = 0; i < transferredItemCount; i++) {
|
||||||
if (!tierItemModifiers.length) {
|
if (!tierItemModifiers.length) {
|
||||||
while (highestItemTier-- && !tierItemModifiers.length) {
|
while (highestItemTier-- && !tierItemModifiers.length) {
|
||||||
@ -3149,19 +3147,15 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
|
|||||||
}
|
}
|
||||||
const randItemIndex = pokemon.randSeedInt(itemModifiers.length);
|
const randItemIndex = pokemon.randSeedInt(itemModifiers.length);
|
||||||
const randItem = itemModifiers[randItemIndex];
|
const randItem = itemModifiers[randItemIndex];
|
||||||
heldItemTransferPromises.push(globalScene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => {
|
if (globalScene.tryTransferHeldItemModifier(randItem, pokemon, false)) {
|
||||||
if (success) {
|
transferredModifierTypes.push(randItem.type);
|
||||||
transferredModifierTypes.push(randItem.type);
|
itemModifiers.splice(randItemIndex, 1);
|
||||||
itemModifiers.splice(randItemIndex, 1);
|
}
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(heldItemTransferPromises).then(() => {
|
for (const mt of transferredModifierTypes) {
|
||||||
for (const mt of transferredModifierTypes) {
|
globalScene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
|
||||||
globalScene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return !!transferredModifierTypes.length;
|
return !!transferredModifierTypes.length;
|
||||||
}
|
}
|
||||||
@ -3390,7 +3384,7 @@ abstract class EnemyDamageMultiplierModifier extends EnemyPersistentModifier {
|
|||||||
* @returns always `true`
|
* @returns always `true`
|
||||||
*/
|
*/
|
||||||
override apply(multiplier: NumberHolder): boolean {
|
override apply(multiplier: NumberHolder): boolean {
|
||||||
multiplier.value = Math.floor(multiplier.value * Math.pow(this.damageMultiplier, this.getStackCount()));
|
multiplier.value = toDmgValue(multiplier.value * Math.pow(this.damageMultiplier, this.getStackCount()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ export class AddEnemyBuffModifierPhase extends Phase {
|
|||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
globalScene.addEnemyModifier(getEnemyBuffModifierForWave(tier, globalScene.findModifiers(m => m instanceof EnemyPersistentModifier, false)), true, true);
|
globalScene.addEnemyModifier(getEnemyBuffModifierForWave(tier, globalScene.findModifiers(m => m instanceof EnemyPersistentModifier, false)), true, true);
|
||||||
}
|
}
|
||||||
globalScene.updateModifiers(false, true).then(() => this.end());
|
globalScene.updateModifiers(false, true);
|
||||||
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ export class BattleEndPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.updateModifiers().then(() => this.end());
|
globalScene.updateModifiers();
|
||||||
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ import { Species } from "#enums/species";
|
|||||||
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { Nature } from "#enums/nature";
|
||||||
|
|
||||||
export class EncounterPhase extends BattlePhase {
|
export class EncounterPhase extends BattlePhase {
|
||||||
private loaded: boolean;
|
private loaded: boolean;
|
||||||
@ -156,7 +157,31 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
|
|
||||||
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
loadEnemyAssets.push(enemyPokemon.loadAssets());
|
||||||
|
|
||||||
console.log(`Pokemon: ${getPokemonNameWithAffix(enemyPokemon)}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`);
|
const stats: string[] = [
|
||||||
|
`HP: ${enemyPokemon.stats[0]} (${enemyPokemon.ivs[0]})`,
|
||||||
|
` Atk: ${enemyPokemon.stats[1]} (${enemyPokemon.ivs[1]})`,
|
||||||
|
` Def: ${enemyPokemon.stats[2]} (${enemyPokemon.ivs[2]})`,
|
||||||
|
` Spatk: ${enemyPokemon.stats[3]} (${enemyPokemon.ivs[3]})`,
|
||||||
|
` Spdef: ${enemyPokemon.stats[4]} (${enemyPokemon.ivs[4]})`,
|
||||||
|
` Spd: ${enemyPokemon.stats[5]} (${enemyPokemon.ivs[5]})`,
|
||||||
|
];
|
||||||
|
const moveset: string[] = [];
|
||||||
|
enemyPokemon.getMoveset().forEach((move) => {
|
||||||
|
moveset.push(move!.getName());
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Pokemon: ${getPokemonNameWithAffix(enemyPokemon)}`,
|
||||||
|
`| Species ID: ${enemyPokemon.species.speciesId}`,
|
||||||
|
`| Nature: ${Nature[enemyPokemon.getNature()]}`,
|
||||||
|
);
|
||||||
|
console.log(`Stats (IVs): ${stats}`);
|
||||||
|
console.log(
|
||||||
|
`Ability: ${enemyPokemon.getAbility().name}`,
|
||||||
|
`| Passive Ability${enemyPokemon.isBoss() ? "" : " (inactive)"}: ${enemyPokemon.getPassiveAbility().name}`,
|
||||||
|
`${enemyPokemon.isBoss() ? `| Boss Bars: ${enemyPokemon.bossSegments}` : ""}`
|
||||||
|
);
|
||||||
|
console.log("Moveset:", moveset);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,16 +12,22 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase {
|
|||||||
doReward(): Promise<void> {
|
doReward(): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const newModifier = this.modifierType.newModifier();
|
const newModifier = this.modifierType.newModifier();
|
||||||
globalScene.addModifier(newModifier).then(() => {
|
globalScene.addModifier(newModifier);
|
||||||
// Sound loaded into game as is
|
// Sound loaded into game as is
|
||||||
globalScene.playSound("level_up_fanfare");
|
globalScene.playSound("level_up_fanfare");
|
||||||
globalScene.ui.setMode(Mode.MESSAGE);
|
globalScene.ui.setMode(Mode.MESSAGE);
|
||||||
globalScene.ui.fadeIn(250).then(() => {
|
globalScene.ui.fadeIn(250).then(() => {
|
||||||
globalScene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => {
|
globalScene.ui.showText(
|
||||||
|
i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }),
|
||||||
|
null,
|
||||||
|
() => {
|
||||||
globalScene.time.delayedCall(1500, () => globalScene.arenaBg.setVisible(true));
|
globalScene.time.delayedCall(1500, () => globalScene.arenaBg.setVisible(true));
|
||||||
resolve();
|
resolve();
|
||||||
}, null, true, 1500);
|
},
|
||||||
});
|
null,
|
||||||
|
true,
|
||||||
|
1500,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
20
src/phases/load-move-anim-phase.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||||
|
import type { Moves } from "#enums/moves";
|
||||||
|
import { Phase } from "#app/phase";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase for synchronous move animation loading.
|
||||||
|
* Should be used when a move invokes another move that
|
||||||
|
* isn't already loaded (e.g. for Metronome)
|
||||||
|
*/
|
||||||
|
export class LoadMoveAnimPhase extends Phase {
|
||||||
|
constructor(protected moveId: Moves) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override start(): void {
|
||||||
|
initMoveAnim(this.moveId)
|
||||||
|
.then(() => loadMoveAnimAssets([ this.moveId ], true))
|
||||||
|
.then(() => this.end());
|
||||||
|
}
|
||||||
|
}
|
@ -22,10 +22,9 @@ export class ModifierRewardPhase extends BattlePhase {
|
|||||||
doReward(): Promise<void> {
|
doReward(): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const newModifier = this.modifierType.newModifier();
|
const newModifier = this.modifierType.newModifier();
|
||||||
globalScene.addModifier(newModifier).then(() => {
|
globalScene.addModifier(newModifier);
|
||||||
globalScene.playSound("item_fanfare");
|
globalScene.playSound("item_fanfare");
|
||||||
globalScene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true);
|
globalScene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
src/phases/move-anim-phase.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { MoveAnim } from "#app/data/battle-anims";
|
||||||
|
import { Phase } from "#app/phase";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays the given {@linkcode MoveAnim} sequentially.
|
||||||
|
*/
|
||||||
|
export class MoveAnimPhase<Anim extends MoveAnim> extends Phase {
|
||||||
|
constructor(
|
||||||
|
protected anim: Anim,
|
||||||
|
protected onSubstitute: boolean = false,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override start(): void {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
this.anim.play(this.onSubstitute, () => this.end());
|
||||||
|
}
|
||||||
|
}
|
@ -44,10 +44,9 @@ export class MoveChargePhase extends PokemonPhase {
|
|||||||
new MoveChargeAnim(move.chargeAnim, move.id, user).play(false, () => {
|
new MoveChargeAnim(move.chargeAnim, move.id, user).play(false, () => {
|
||||||
move.showChargeText(user, target);
|
move.showChargeText(user, target);
|
||||||
|
|
||||||
applyMoveChargeAttrs(MoveEffectAttr, user, target, move).then(() => {
|
applyMoveChargeAttrs(MoveEffectAttr, user, target, move);
|
||||||
user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id);
|
user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id);
|
||||||
this.end();
|
this.end();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ import {
|
|||||||
PokemonMultiHitModifier,
|
PokemonMultiHitModifier,
|
||||||
} from "#app/modifier/modifier";
|
} from "#app/modifier/modifier";
|
||||||
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
import { BooleanHolder, executeIf, isNullOrUndefined, NumberHolder } from "#app/utils";
|
import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils";
|
||||||
import { type nil } from "#app/utils";
|
import { type nil } from "#app/utils";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import type { Moves } from "#enums/moves";
|
import type { Moves } from "#enums/moves";
|
||||||
@ -143,86 +143,86 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const move = this.move.getMove();
|
const move = this.move.getMove();
|
||||||
|
|
||||||
// Assume single target for override
|
// Assume single target for override
|
||||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.move.virtual).then(() => {
|
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.move.virtual);
|
||||||
// If other effects were overriden, stop this phase before they can be applied
|
|
||||||
if (overridden.value) {
|
// If other effects were overriden, stop this phase before they can be applied
|
||||||
return this.end();
|
if (overridden.value) {
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||||
|
|
||||||
|
// If the user is acting again (such as due to Instruct), reset hitsLeft/hitCount so that
|
||||||
|
// the move executes correctly (ensures all hits of a multi-hit are properly calculated)
|
||||||
|
if (user.turnData.hitsLeft === 0 && user.turnData.hitCount > 0 && user.turnData.extraTurns > 0) {
|
||||||
|
user.turnData.hitsLeft = -1;
|
||||||
|
user.turnData.hitCount = 0;
|
||||||
|
user.turnData.extraTurns--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this phase is for the first hit of the invoked move,
|
||||||
|
* resolve the move's total hit count. This block combines the
|
||||||
|
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
||||||
|
*/
|
||||||
|
if (user.turnData.hitsLeft === -1) {
|
||||||
|
const hitCount = new NumberHolder(1);
|
||||||
|
// Assume single target for multi hit
|
||||||
|
applyMoveAttrs(MultiHitAttr, user, this.getFirstTarget() ?? null, move, hitCount);
|
||||||
|
// If Parental Bond is applicable, add another hit
|
||||||
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
|
||||||
|
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
||||||
|
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
|
||||||
|
// Set the user's relevant turnData fields to reflect the final hit count
|
||||||
|
user.turnData.hitCount = hitCount.value;
|
||||||
|
user.turnData.hitsLeft = hitCount.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log to be entered into the user's move history once the move result is resolved.
|
||||||
|
* Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully
|
||||||
|
* used in the sense of "Does it have an effect on the user?".
|
||||||
|
*/
|
||||||
|
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores results of hit checks of the invoked move against all targets, organized by battler index.
|
||||||
|
* @see {@linkcode hitCheck}
|
||||||
|
*/
|
||||||
|
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
|
||||||
|
const hasActiveTargets = targets.some(t => t.isActive(true));
|
||||||
|
|
||||||
|
/** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */
|
||||||
|
const isImmune = targets[0]?.hasAbilityWithAttr(TypeImmunityAbAttr)
|
||||||
|
&& (targets[0]?.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
||||||
|
&& !targets[0]?.getTag(SemiInvulnerableTag);
|
||||||
|
|
||||||
|
const mayBounce = move.hasFlag(MoveFlags.REFLECTABLE) && !this.reflected && targets.some(t => t.hasAbilityWithAttr(ReflectStatusMoveAbAttr) || !!t.getTag(BattlerTagType.MAGIC_COAT));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no targets are left for the move to hit (FAIL), or the invoked move is non-reflectable, single-target
|
||||||
|
* (and not random target) and failed the hit check against its target (MISS), log the move
|
||||||
|
* as FAILed or MISSed (depending on the conditions above) and end this phase.
|
||||||
|
*/
|
||||||
|
if (!hasActiveTargets || (!mayBounce && !move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag) && !isImmune)) {
|
||||||
|
this.stopMultiHit();
|
||||||
|
if (hasActiveTargets) {
|
||||||
|
globalScene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getFirstTarget() ? getPokemonNameWithAffix(this.getFirstTarget()!) : "" }));
|
||||||
|
moveHistoryEntry.result = MoveResult.MISS;
|
||||||
|
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
||||||
|
} else {
|
||||||
|
globalScene.queueMessage(i18next.t("battle:attackFailed"));
|
||||||
|
moveHistoryEntry.result = MoveResult.FAIL;
|
||||||
}
|
}
|
||||||
|
user.pushMoveHistory(moveHistoryEntry);
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
|
||||||
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
const playOnEmptyField = globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false;
|
||||||
|
// Move animation only needs one target
|
||||||
// If the user is acting again (such as due to Instruct), reset hitsLeft/hitCount so that
|
new MoveAnim(move.id as Moves, user, this.getFirstTarget()!.getBattlerIndex(), playOnEmptyField).play(
|
||||||
// the move executes correctly (ensures all hits of a multi-hit are properly calculated)
|
move.hitsSubstitute(user, this.getFirstTarget()!),
|
||||||
if (user.turnData.hitsLeft === 0 && user.turnData.hitCount > 0 && user.turnData.extraTurns > 0) {
|
() => {
|
||||||
user.turnData.hitsLeft = -1;
|
|
||||||
user.turnData.hitCount = 0;
|
|
||||||
user.turnData.extraTurns--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this phase is for the first hit of the invoked move,
|
|
||||||
* resolve the move's total hit count. This block combines the
|
|
||||||
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
|
||||||
*/
|
|
||||||
if (user.turnData.hitsLeft === -1) {
|
|
||||||
const hitCount = new NumberHolder(1);
|
|
||||||
// Assume single target for multi hit
|
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getFirstTarget() ?? null, move, hitCount);
|
|
||||||
// If Parental Bond is applicable, add another hit
|
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
|
|
||||||
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
|
||||||
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
|
|
||||||
// Set the user's relevant turnData fields to reflect the final hit count
|
|
||||||
user.turnData.hitCount = hitCount.value;
|
|
||||||
user.turnData.hitsLeft = hitCount.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log to be entered into the user's move history once the move result is resolved.
|
|
||||||
* Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully
|
|
||||||
* used in the sense of "Does it have an effect on the user?".
|
|
||||||
*/
|
|
||||||
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores results of hit checks of the invoked move against all targets, organized by battler index.
|
|
||||||
* @see {@linkcode hitCheck}
|
|
||||||
*/
|
|
||||||
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
|
|
||||||
const hasActiveTargets = targets.some(t => t.isActive(true));
|
|
||||||
|
|
||||||
/** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */
|
|
||||||
const isImmune = targets[0]?.hasAbilityWithAttr(TypeImmunityAbAttr)
|
|
||||||
&& (targets[0]?.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
|
||||||
&& !targets[0]?.getTag(SemiInvulnerableTag);
|
|
||||||
|
|
||||||
const mayBounce = move.hasFlag(MoveFlags.REFLECTABLE) && !this.reflected && targets.some(t => t.hasAbilityWithAttr(ReflectStatusMoveAbAttr) || !!t.getTag(BattlerTagType.MAGIC_COAT));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If no targets are left for the move to hit (FAIL), or the invoked move is non-reflectable, single-target
|
|
||||||
* (and not random target) and failed the hit check against its target (MISS), log the move
|
|
||||||
* as FAILed or MISSed (depending on the conditions above) and end this phase.
|
|
||||||
*/
|
|
||||||
if (!hasActiveTargets || (!mayBounce && !move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag) && !isImmune)) {
|
|
||||||
this.stopMultiHit();
|
|
||||||
if (hasActiveTargets) {
|
|
||||||
globalScene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getFirstTarget() ? getPokemonNameWithAffix(this.getFirstTarget()!) : "" }));
|
|
||||||
moveHistoryEntry.result = MoveResult.MISS;
|
|
||||||
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
|
||||||
} else {
|
|
||||||
globalScene.queueMessage(i18next.t("battle:attackFailed"));
|
|
||||||
moveHistoryEntry.result = MoveResult.FAIL;
|
|
||||||
}
|
|
||||||
user.pushMoveHistory(moveHistoryEntry);
|
|
||||||
return this.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** All move effect attributes are chained together in this array to be applied asynchronously. */
|
|
||||||
const applyAttrs: Promise<void>[] = [];
|
|
||||||
|
|
||||||
const playOnEmptyField = globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false;
|
|
||||||
// Move animation only needs one target
|
|
||||||
new MoveAnim(move.id as Moves, user, this.getFirstTarget()!.getBattlerIndex(), playOnEmptyField).play(move.hitsSubstitute(user, this.getFirstTarget()!), () => {
|
|
||||||
/** Has the move successfully hit a target (for damage) yet? */
|
/** Has the move successfully hit a target (for damage) yet? */
|
||||||
let hasHit: boolean = false;
|
let hasHit: boolean = false;
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Does this phase represent the invoked move's first strike? */
|
/** Does this phase represent the invoked move's first strike? */
|
||||||
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
const firstHit = user.turnData.hitsLeft === user.turnData.hitCount;
|
||||||
|
|
||||||
// Only log the move's result on the first strike
|
// Only log the move's result on the first strike
|
||||||
if (firstHit) {
|
if (firstHit) {
|
||||||
@ -363,7 +363,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Does this phase represent the invoked move's last strike? */
|
/** Does this phase represent the invoked move's last strike? */
|
||||||
const lastHit = (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive());
|
const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the user can change forms by using the invoked move,
|
* If the user can change forms by using the invoked move,
|
||||||
@ -381,43 +381,29 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
applyFilteredMoveAttrs(
|
||||||
* Create a Promise that applies *all* effects from the invoked move's MoveEffectAttrs.
|
(attr: MoveAttr) =>
|
||||||
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
|
attr instanceof MoveEffectAttr &&
|
||||||
* type requires different conditions to be met with respect to the move's hit result.
|
attr.trigger === MoveEffectTrigger.PRE_APPLY &&
|
||||||
*/
|
(!attr.firstHitOnly || firstHit) &&
|
||||||
const k = new Promise<void>((resolve) => {
|
(!attr.lastHitOnly || lastHit) &&
|
||||||
//Start promise chain and apply PRE_APPLY move attributes
|
hitResult !== HitResult.NO_EFFECT,
|
||||||
let promiseChain: Promise<void | null> = applyFilteredMoveAttrs((attr: MoveAttr) =>
|
user,
|
||||||
attr instanceof MoveEffectAttr
|
target,
|
||||||
&& attr.trigger === MoveEffectTrigger.PRE_APPLY
|
move,
|
||||||
&& (!attr.firstHitOnly || firstHit)
|
);
|
||||||
&& (!attr.lastHitOnly || lastHit)
|
|
||||||
&& hitResult !== HitResult.NO_EFFECT, user, target, move);
|
|
||||||
|
|
||||||
/** Don't complete if the move failed */
|
if (hitResult !== HitResult.FAIL) {
|
||||||
if (hitResult === HitResult.FAIL) {
|
this.applySelfTargetEffects(user, target, firstHit, lastHit);
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Apply Move/Ability Effects in correct order */
|
|
||||||
promiseChain = promiseChain
|
|
||||||
.then(this.applySelfTargetEffects(user, target, firstHit, lastHit));
|
|
||||||
|
|
||||||
if (hitResult !== HitResult.NO_EFFECT) {
|
if (hitResult !== HitResult.NO_EFFECT) {
|
||||||
promiseChain
|
this.applyPostApplyEffects(user, target, firstHit, lastHit);
|
||||||
.then(this.applyPostApplyEffects(user, target, firstHit, lastHit))
|
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
||||||
.then(this.applyHeldItemFlinchCheck(user, target, dealsDamage))
|
this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget);
|
||||||
.then(this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget))
|
|
||||||
.then(() => resolve());
|
|
||||||
} else {
|
} else {
|
||||||
promiseChain
|
applyMoveAttrs(NoEffectAttr, user, null, move);
|
||||||
.then(() => applyMoveAttrs(NoEffectAttr, user, null, move))
|
|
||||||
.then(resolve);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
applyAttrs.push(k);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply queued phases
|
// Apply queued phases
|
||||||
@ -425,41 +411,35 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
globalScene.appendToPhase(queuedPhases, MoveEndPhase);
|
globalScene.appendToPhase(queuedPhases, MoveEndPhase);
|
||||||
}
|
}
|
||||||
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
|
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
|
||||||
const postTarget = (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) ?
|
if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) {
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
|
applyFilteredMoveAttrs(
|
||||||
null;
|
(attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET,
|
||||||
|
user,
|
||||||
if (postTarget) {
|
null,
|
||||||
if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after
|
move,
|
||||||
applyAttrs[applyAttrs.length - 1].then(() => postTarget);
|
);
|
||||||
} else { // Otherwise, push a new asynchronous move effect
|
|
||||||
applyAttrs.push(postTarget);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all move effects to finish applying, then end this phase
|
/**
|
||||||
Promise.allSettled(applyAttrs).then(() => {
|
* Remove the target's substitute (if it exists and has expired)
|
||||||
/**
|
* after all targeted effects have applied.
|
||||||
* Remove the target's substitute (if it exists and has expired)
|
* This prevents blocked effects from applying until after this hit resolves.
|
||||||
* after all targeted effects have applied.
|
*/
|
||||||
* This prevents blocked effects from applying until after this hit resolves.
|
targets.forEach((target) => {
|
||||||
*/
|
const substitute = target.getTag(SubstituteTag);
|
||||||
targets.forEach(target => {
|
if (substitute && substitute.hp <= 0) {
|
||||||
const substitute = target.getTag(SubstituteTag);
|
target.lapseTag(BattlerTagType.SUBSTITUTE);
|
||||||
if (substitute && substitute.hp <= 0) {
|
|
||||||
target.lapseTag(BattlerTagType.SUBSTITUTE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const moveType = user.getMoveType(move, true);
|
|
||||||
if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
|
|
||||||
user.stellarTypesBoosted.push(moveType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.end();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
const moveType = user.getMoveType(move, true);
|
||||||
|
if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
|
||||||
|
user.stellarTypesBoosted.push(moveType);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.end();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override end(): void {
|
public override end(): void {
|
||||||
@ -500,8 +480,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applySelfTargetEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): () => Promise<void | null> {
|
protected applySelfTargetEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): void {
|
||||||
return () => applyFilteredMoveAttrs((attr: MoveAttr) =>
|
applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
attr instanceof MoveEffectAttr
|
attr instanceof MoveEffectAttr
|
||||||
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||||
&& attr.selfTarget
|
&& attr.selfTarget
|
||||||
@ -518,8 +498,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyPostApplyEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): () => Promise<void | null> {
|
protected applyPostApplyEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): void {
|
||||||
return () => applyFilteredMoveAttrs((attr: MoveAttr) =>
|
applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
attr instanceof MoveEffectAttr
|
attr instanceof MoveEffectAttr
|
||||||
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||||
&& !attr.selfTarget
|
&& !attr.selfTarget
|
||||||
@ -537,8 +517,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyOnHitEffects(user: Pokemon, target: Pokemon, firstHit : boolean, lastHit: boolean, firstTarget: boolean): Promise<void> {
|
protected applyOnHitEffects(user: Pokemon, target: Pokemon, firstHit : boolean, lastHit: boolean, firstTarget: boolean): void {
|
||||||
return applyFilteredMoveAttrs((attr: MoveAttr) =>
|
applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
attr instanceof MoveEffectAttr
|
attr instanceof MoveEffectAttr
|
||||||
&& attr.trigger === MoveEffectTrigger.HIT
|
&& attr.trigger === MoveEffectTrigger.HIT
|
||||||
&& (!attr.firstHitOnly || firstHit)
|
&& (!attr.firstHitOnly || firstHit)
|
||||||
@ -554,21 +534,18 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
||||||
* @returns a `Promise` intended to be passed into a `then()` call.
|
* @returns a `Promise` intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): Promise<void | null> {
|
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void {
|
||||||
return executeIf(!target.isFainted() || target.canApplyAbility(), () =>
|
if (!target.isFainted() || target.canApplyAbility()) {
|
||||||
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult)
|
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult);
|
||||||
.then(() => {
|
|
||||||
|
|
||||||
if (!this.move.getMove().hitsSubstitute(user, target)) {
|
if (!this.move.getMove().hitsSubstitute(user, target)) {
|
||||||
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
||||||
globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
|
globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -583,17 +560,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applySuccessfulAttackEffects(user: Pokemon, target: Pokemon, firstHit : boolean, lastHit: boolean, isProtected : boolean, hitResult: HitResult, firstTarget: boolean) : () => Promise<void | null> {
|
protected applySuccessfulAttackEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean, isProtected: boolean, hitResult: HitResult, firstTarget: boolean): void {
|
||||||
return () => executeIf(!isProtected, () =>
|
if (!isProtected) {
|
||||||
this.applyOnHitEffects(user, target, firstHit, lastHit, firstTarget).then(() =>
|
this.applyOnHitEffects(user, target, firstHit, lastHit, firstTarget);
|
||||||
this.applyOnGetHitAbEffects(user, target, hitResult)).then(() =>
|
this.applyOnGetHitAbEffects(user, target, hitResult);
|
||||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult)).then(() => { // Item Stealing Effects
|
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult);
|
||||||
|
if (this.move.getMove() instanceof AttackMove) {
|
||||||
if (this.move.getMove() instanceof AttackMove) {
|
globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
||||||
globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -603,20 +578,18 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param dealsDamage - `true` if the attempted move successfully dealt damage
|
* @param dealsDamage - `true` if the attempted move successfully dealt damage
|
||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean) : () => void {
|
protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean) : void {
|
||||||
return () => {
|
if (this.move.getMove().hasAttr(FlinchAttr)) {
|
||||||
if (this.move.getMove().hasAttr(FlinchAttr)) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !this.move.getMove().hitsSubstitute(user, target)) {
|
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !this.move.getMove().hitsSubstitute(user, target)) {
|
||||||
const flinched = new BooleanHolder(false);
|
const flinched = new BooleanHolder(false);
|
||||||
globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||||
if (flinched.value) {
|
if (flinched.value) {
|
||||||
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,9 +22,8 @@ export class MoveHeaderPhase extends BattlePhase {
|
|||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
if (this.canMove()) {
|
if (this.canMove()) {
|
||||||
applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove()).then(() => this.end());
|
applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove());
|
||||||
} else {
|
|
||||||
this.end();
|
|
||||||
}
|
}
|
||||||
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
protected _targets: BattlerIndex[];
|
protected _targets: BattlerIndex[];
|
||||||
protected followUp: boolean;
|
protected followUp: boolean;
|
||||||
protected ignorePp: boolean;
|
protected ignorePp: boolean;
|
||||||
|
protected forcedLast: boolean;
|
||||||
protected failed: boolean = false;
|
protected failed: boolean = false;
|
||||||
protected cancelled: boolean = false;
|
protected cancelled: boolean = false;
|
||||||
protected reflected: boolean = false;
|
protected reflected: boolean = false;
|
||||||
@ -90,7 +91,8 @@ export class MovePhase extends BattlePhase {
|
|||||||
* @param reflected Indicates that the move was reflected by Magic Coat or Magic Bounce.
|
* @param reflected Indicates that the move was reflected by Magic Coat or Magic Bounce.
|
||||||
* Reflected moves cannot be reflected again and will not trigger Dancer.
|
* Reflected moves cannot be reflected again and will not trigger Dancer.
|
||||||
*/
|
*/
|
||||||
constructor(pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp: boolean = false, ignorePp: boolean = false, reflected: boolean = false) {
|
|
||||||
|
constructor(pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp: boolean = false, ignorePp: boolean = false, reflected: boolean = false, forcedLast: boolean = false) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.pokemon = pokemon;
|
this.pokemon = pokemon;
|
||||||
@ -99,6 +101,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.followUp = followUp;
|
this.followUp = followUp;
|
||||||
this.ignorePp = ignorePp;
|
this.ignorePp = ignorePp;
|
||||||
this.reflected = reflected;
|
this.reflected = reflected;
|
||||||
|
this.forcedLast = forcedLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,6 +123,15 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.cancelled = true;
|
this.cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows whether the current move has been forced to the end of the turn
|
||||||
|
* Needed for speed order, see {@linkcode Moves.QUASH}
|
||||||
|
* */
|
||||||
|
public isForcedLast(): boolean {
|
||||||
|
return this.forcedLast;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public start(): void {
|
public start(): void {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
77
src/phases/pokemon-transform-phase.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import type { BattlerIndex } from "#app/battle";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat";
|
||||||
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a Pokemon into another Pokemon on the field.
|
||||||
|
* Used for Transform (move) and Imposter (ability)
|
||||||
|
*/
|
||||||
|
export class PokemonTransformPhase extends PokemonPhase {
|
||||||
|
protected targetIndex: BattlerIndex;
|
||||||
|
private playSound: boolean;
|
||||||
|
|
||||||
|
constructor(userIndex: BattlerIndex, targetIndex: BattlerIndex, playSound: boolean = false) {
|
||||||
|
super(userIndex);
|
||||||
|
|
||||||
|
this.targetIndex = targetIndex;
|
||||||
|
this.playSound = playSound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override start(): void {
|
||||||
|
const user = this.getPokemon();
|
||||||
|
const target = globalScene.getField(true).find((p) => p.getBattlerIndex() === this.targetIndex);
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
user.summonData.speciesForm = target.getSpeciesForm();
|
||||||
|
user.summonData.ability = target.getAbility().id;
|
||||||
|
user.summonData.gender = target.getGender();
|
||||||
|
|
||||||
|
// Power Trick's effect is removed after using Transform
|
||||||
|
user.removeTag(BattlerTagType.POWER_TRICK);
|
||||||
|
|
||||||
|
// Copy all stats (except HP)
|
||||||
|
for (const s of EFFECTIVE_STATS) {
|
||||||
|
user.setStat(s, target.getStat(s, false), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all stat stages
|
||||||
|
for (const s of BATTLE_STATS) {
|
||||||
|
user.setStatStage(s, target.getStatStage(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.summonData.moveset = target.getMoveset().map((m) => {
|
||||||
|
if (m) {
|
||||||
|
// If PP value is less than 5, do nothing. If greater, we need to reduce the value to 5.
|
||||||
|
return new PokemonMove(m.moveId, 0, 0, false, Math.min(m.getMove().pp, 5));
|
||||||
|
} else {
|
||||||
|
console.warn(`Transform: somehow iterating over a ${m} value when copying moveset!`);
|
||||||
|
return new PokemonMove(Moves.NONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
user.summonData.types = target.getTypes();
|
||||||
|
|
||||||
|
const promises = [ user.updateInfo() ];
|
||||||
|
|
||||||
|
if (this.playSound) {
|
||||||
|
globalScene.playSound("battle_anims/PRSFX- Transform");
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
user.loadAssets(false).then(() => {
|
||||||
|
user.playAnim();
|
||||||
|
user.updateInfo();
|
||||||
|
// If the new ability activates immediately, it needs to happen after all the transform animations
|
||||||
|
user.setTempAbility(target.getAbility());
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
Promise.allSettled(promises).then(() => this.end());
|
||||||
|
}
|
||||||
|
}
|
@ -27,12 +27,10 @@ export class PostSummonPhase extends PokemonPhase {
|
|||||||
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPostSummonAbAttrs(PostSummonAbAttr, pokemon)
|
applyPostSummonAbAttrs(PostSummonAbAttr, pokemon);
|
||||||
.then(() => {
|
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
field.forEach((p) => applyAbAttrs(CommanderAbAttr, p, null, false));
|
||||||
field.forEach((p) => applyAbAttrs(CommanderAbAttr, p, null, false));
|
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
src/phases/revival-blessing-phase.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { SwitchType } from "#enums/switch-type";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { PartyOption } from "#app/ui/party-ui-handler";
|
||||||
|
import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
|
import { BattlePhase } from "#app/phases/battle-phase";
|
||||||
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
|
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||||
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Party UI and handles the effect of Revival Blessing
|
||||||
|
* when used by one of the player's Pokemon.
|
||||||
|
*/
|
||||||
|
export class RevivalBlessingPhase extends BattlePhase {
|
||||||
|
constructor(protected user: PlayerPokemon) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override start(): void {
|
||||||
|
globalScene.ui.setMode(
|
||||||
|
Mode.PARTY,
|
||||||
|
PartyUiMode.REVIVAL_BLESSING,
|
||||||
|
this.user.getFieldIndex(),
|
||||||
|
(slotIndex: integer, option: PartyOption) => {
|
||||||
|
if (slotIndex >= 0 && slotIndex < 6) {
|
||||||
|
const pokemon = globalScene.getPlayerParty()[slotIndex];
|
||||||
|
if (!pokemon || !pokemon.isFainted()) {
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.resetTurnData();
|
||||||
|
pokemon.resetStatus();
|
||||||
|
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||||
|
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name }), 0, true);
|
||||||
|
|
||||||
|
if (globalScene.currentBattle.double && globalScene.getPlayerParty().length > 1) {
|
||||||
|
const allyPokemon = this.user.getAlly();
|
||||||
|
if (slotIndex <= 1) {
|
||||||
|
// Revived ally pokemon
|
||||||
|
globalScene.unshiftPhase(
|
||||||
|
new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, true),
|
||||||
|
);
|
||||||
|
globalScene.unshiftPhase(new ToggleDoublePositionPhase(true));
|
||||||
|
} else if (allyPokemon.isFainted()) {
|
||||||
|
// Revived party pokemon, and ally pokemon is fainted
|
||||||
|
globalScene.unshiftPhase(
|
||||||
|
new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, true),
|
||||||
|
);
|
||||||
|
globalScene.unshiftPhase(new ToggleDoublePositionPhase(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globalScene.ui.setMode(Mode.MESSAGE).then(() => this.end());
|
||||||
|
},
|
||||||
|
PartyUiHandler.FilterFainted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -17,17 +17,16 @@ export class RibbonModifierRewardPhase extends ModifierRewardPhase {
|
|||||||
doReward(): Promise<void> {
|
doReward(): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const newModifier = this.modifierType.newModifier();
|
const newModifier = this.modifierType.newModifier();
|
||||||
globalScene.addModifier(newModifier).then(() => {
|
globalScene.addModifier(newModifier);
|
||||||
globalScene.playSound("level_up_fanfare");
|
globalScene.playSound("level_up_fanfare");
|
||||||
globalScene.ui.setMode(Mode.MESSAGE);
|
globalScene.ui.setMode(Mode.MESSAGE);
|
||||||
globalScene.ui.showText(i18next.t("battle:beatModeFirstTime", {
|
globalScene.ui.showText(i18next.t("battle:beatModeFirstTime", {
|
||||||
speciesName: this.species.name,
|
speciesName: this.species.name,
|
||||||
gameMode: globalScene.gameMode.getName(),
|
gameMode: globalScene.gameMode.getName(),
|
||||||
newModifier: newModifier?.type.name
|
newModifier: newModifier?.type.name,
|
||||||
}), null, () => {
|
}), null, () => {
|
||||||
resolve();
|
resolve();
|
||||||
}, null, true, 1500);
|
}, null, true, 1500);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,30 +171,21 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cost && !(modifier.type instanceof RememberMoveModifierType)) {
|
if (cost && !(modifier.type instanceof RememberMoveModifierType)) {
|
||||||
result.then(success => {
|
if (result) {
|
||||||
if (success) {
|
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
||||||
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
globalScene.money -= cost;
|
||||||
globalScene.money -= cost;
|
globalScene.updateMoneyText();
|
||||||
globalScene.updateMoneyText();
|
globalScene.animateMoneyChanged(false);
|
||||||
globalScene.animateMoneyChanged(false);
|
|
||||||
}
|
|
||||||
globalScene.playSound("se/buy");
|
|
||||||
(globalScene.ui.getHandler() as ModifierSelectUiHandler).updateCostText();
|
|
||||||
} else {
|
|
||||||
globalScene.ui.playError();
|
|
||||||
}
|
}
|
||||||
});
|
globalScene.playSound("se/buy");
|
||||||
} else {
|
(globalScene.ui.getHandler() as ModifierSelectUiHandler).updateCostText();
|
||||||
const doEnd = () => {
|
|
||||||
globalScene.ui.clearText();
|
|
||||||
globalScene.ui.setMode(Mode.MESSAGE);
|
|
||||||
super.end();
|
|
||||||
};
|
|
||||||
if (result instanceof Promise) {
|
|
||||||
result.then(() => doEnd());
|
|
||||||
} else {
|
} else {
|
||||||
doEnd();
|
globalScene.ui.playError();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
globalScene.ui.clearText();
|
||||||
|
globalScene.ui.setMode(Mode.MESSAGE);
|
||||||
|
super.end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -304,7 +295,7 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addModifier(modifier: Modifier): Promise<boolean> {
|
addModifier(modifier: Modifier): boolean {
|
||||||
return globalScene.addModifier(modifier, false, true);
|
return globalScene.addModifier(modifier, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import type { Starter } from "#app/ui/starter-select-ui-handler";
|
|||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import type { Species } from "#enums/species";
|
import type { Species } from "#enums/species";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
|
import * as Utils from "../utils";
|
||||||
|
|
||||||
export class SelectStarterPhase extends Phase {
|
export class SelectStarterPhase extends Phase {
|
||||||
|
|
||||||
@ -79,6 +80,12 @@ export class SelectStarterPhase extends Phase {
|
|||||||
starterPokemon.nickname = starter.nickname;
|
starterPokemon.nickname = starter.nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Utils.isNullOrUndefined(starter.teraType)) {
|
||||||
|
starterPokemon.teraType = starter.teraType;
|
||||||
|
} else {
|
||||||
|
starterPokemon.teraType = starterPokemon.species.type1;
|
||||||
|
}
|
||||||
|
|
||||||
if (globalScene.gameMode.isSplicedOnly || Overrides.STARTER_FUSION_OVERRIDE) {
|
if (globalScene.gameMode.isSplicedOnly || Overrides.STARTER_FUSION_OVERRIDE) {
|
||||||
starterPokemon.generateFusionSpecies(true);
|
starterPokemon.generateFusionSpecies(true);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-e
|
|||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
import { ArenaTrapTag } from "#app/data/arena-tag";
|
import { ArenaTrapTag } from "#app/data/arena-tag";
|
||||||
|
import type { Type } from "#enums/type";
|
||||||
|
|
||||||
export const defaultStarterSpecies: Species[] = [
|
export const defaultStarterSpecies: Species[] = [
|
||||||
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
||||||
@ -229,6 +230,7 @@ export interface StarterAttributes {
|
|||||||
shiny?: boolean;
|
shiny?: boolean;
|
||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
|
tera?: Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StarterPreferences {
|
export interface StarterPreferences {
|
||||||
|
@ -21,7 +21,7 @@ export enum SettingGamepad {
|
|||||||
Button_Cycle_Gender = "BUTTON_CYCLE_GENDER",
|
Button_Cycle_Gender = "BUTTON_CYCLE_GENDER",
|
||||||
Button_Cycle_Ability = "BUTTON_CYCLE_ABILITY",
|
Button_Cycle_Ability = "BUTTON_CYCLE_ABILITY",
|
||||||
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
|
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
|
||||||
Button_Cycle_Variant = "BUTTON_CYCLE_VARIANT",
|
Button_Cycle_Tera = "BUTTON_CYCLE_TERA",
|
||||||
Button_Speed_Up = "BUTTON_SPEED_UP",
|
Button_Speed_Up = "BUTTON_SPEED_UP",
|
||||||
Button_Slow_Down = "BUTTON_SLOW_DOWN",
|
Button_Slow_Down = "BUTTON_SLOW_DOWN",
|
||||||
Button_Submit = "BUTTON_SUBMIT",
|
Button_Submit = "BUTTON_SUBMIT",
|
||||||
@ -45,7 +45,7 @@ export const settingGamepadOptions = {
|
|||||||
[SettingGamepad.Button_Cycle_Gender]: [ `KEY ${Button.CYCLE_GENDER.toString()}`, pressAction ],
|
[SettingGamepad.Button_Cycle_Gender]: [ `KEY ${Button.CYCLE_GENDER.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Cycle_Ability]: [ `KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction ],
|
[SettingGamepad.Button_Cycle_Ability]: [ `KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
[SettingGamepad.Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Cycle_Variant]: [ `KEY ${Button.V.toString()}`, pressAction ],
|
[SettingGamepad.Button_Cycle_Tera]: [ `KEY ${Button.CYCLE_TERA.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
[SettingGamepad.Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Slow_Down]: [ `KEY ${Button.SLOW_DOWN.toString()}`, pressAction ],
|
[SettingGamepad.Button_Slow_Down]: [ `KEY ${Button.SLOW_DOWN.toString()}`, pressAction ],
|
||||||
[SettingGamepad.Button_Submit]: [ `KEY ${Button.SUBMIT.toString()}`, pressAction ],
|
[SettingGamepad.Button_Submit]: [ `KEY ${Button.SUBMIT.toString()}`, pressAction ],
|
||||||
@ -67,7 +67,7 @@ export const settingGamepadDefaults = {
|
|||||||
[SettingGamepad.Button_Cycle_Gender]: 0,
|
[SettingGamepad.Button_Cycle_Gender]: 0,
|
||||||
[SettingGamepad.Button_Cycle_Ability]: 0,
|
[SettingGamepad.Button_Cycle_Ability]: 0,
|
||||||
[SettingGamepad.Button_Cycle_Nature]: 0,
|
[SettingGamepad.Button_Cycle_Nature]: 0,
|
||||||
[SettingGamepad.Button_Cycle_Variant]: 0,
|
[SettingGamepad.Button_Cycle_Tera]: 0,
|
||||||
[SettingGamepad.Button_Speed_Up]: 0,
|
[SettingGamepad.Button_Speed_Up]: 0,
|
||||||
[SettingGamepad.Button_Slow_Down]: 0,
|
[SettingGamepad.Button_Slow_Down]: 0,
|
||||||
[SettingGamepad.Button_Submit]: 0,
|
[SettingGamepad.Button_Submit]: 0,
|
||||||
@ -96,7 +96,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole
|
|||||||
case SettingGamepad.Button_Cycle_Gender:
|
case SettingGamepad.Button_Cycle_Gender:
|
||||||
case SettingGamepad.Button_Cycle_Ability:
|
case SettingGamepad.Button_Cycle_Ability:
|
||||||
case SettingGamepad.Button_Cycle_Nature:
|
case SettingGamepad.Button_Cycle_Nature:
|
||||||
case SettingGamepad.Button_Cycle_Variant:
|
case SettingGamepad.Button_Cycle_Tera:
|
||||||
case SettingGamepad.Button_Speed_Up:
|
case SettingGamepad.Button_Speed_Up:
|
||||||
case SettingGamepad.Button_Slow_Down:
|
case SettingGamepad.Button_Slow_Down:
|
||||||
case SettingGamepad.Button_Submit:
|
case SettingGamepad.Button_Submit:
|
||||||
|
@ -32,8 +32,8 @@ export enum SettingKeyboard {
|
|||||||
Alt_Button_Cycle_Ability = "ALT_BUTTON_CYCLE_ABILITY",
|
Alt_Button_Cycle_Ability = "ALT_BUTTON_CYCLE_ABILITY",
|
||||||
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
|
Button_Cycle_Nature = "BUTTON_CYCLE_NATURE",
|
||||||
Alt_Button_Cycle_Nature = "ALT_BUTTON_CYCLE_NATURE",
|
Alt_Button_Cycle_Nature = "ALT_BUTTON_CYCLE_NATURE",
|
||||||
Button_Cycle_Variant = "BUTTON_CYCLE_VARIANT",
|
Button_Cycle_Tera = "BUTTON_CYCLE_TERA",
|
||||||
Alt_Button_Cycle_Variant = "ALT_BUTTON_CYCLE_VARIANT",
|
Alt_Button_Cycle_Tera = "ALT_BUTTON_CYCLE_TERA",
|
||||||
Button_Speed_Up = "BUTTON_SPEED_UP",
|
Button_Speed_Up = "BUTTON_SPEED_UP",
|
||||||
Alt_Button_Speed_Up = "ALT_BUTTON_SPEED_UP",
|
Alt_Button_Speed_Up = "ALT_BUTTON_SPEED_UP",
|
||||||
Button_Slow_Down = "BUTTON_SLOW_DOWN",
|
Button_Slow_Down = "BUTTON_SLOW_DOWN",
|
||||||
@ -73,8 +73,8 @@ export const settingKeyboardOptions = {
|
|||||||
[SettingKeyboard.Alt_Button_Cycle_Ability]: [ `KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction ],
|
[SettingKeyboard.Alt_Button_Cycle_Ability]: [ `KEY ${Button.CYCLE_ABILITY.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
[SettingKeyboard.Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
[SettingKeyboard.Alt_Button_Cycle_Nature]: [ `KEY ${Button.CYCLE_NATURE.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Button_Cycle_Variant]: [ `KEY ${Button.V.toString()}`, pressAction ],
|
[SettingKeyboard.Button_Cycle_Tera]: [ `KEY ${Button.CYCLE_TERA.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Variant]: [ `KEY ${Button.V.toString()}`, pressAction ],
|
[SettingKeyboard.Alt_Button_Cycle_Tera]: [ `KEY ${Button.CYCLE_TERA.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
[SettingKeyboard.Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Alt_Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
[SettingKeyboard.Alt_Button_Speed_Up]: [ `KEY ${Button.SPEED_UP.toString()}`, pressAction ],
|
||||||
[SettingKeyboard.Button_Slow_Down]: [ `KEY ${Button.SLOW_DOWN.toString()}`, pressAction ],
|
[SettingKeyboard.Button_Slow_Down]: [ `KEY ${Button.SLOW_DOWN.toString()}`, pressAction ],
|
||||||
@ -112,8 +112,8 @@ export const settingKeyboardDefaults = {
|
|||||||
[SettingKeyboard.Alt_Button_Cycle_Ability]: 0,
|
[SettingKeyboard.Alt_Button_Cycle_Ability]: 0,
|
||||||
[SettingKeyboard.Button_Cycle_Nature]: 0,
|
[SettingKeyboard.Button_Cycle_Nature]: 0,
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Nature]: 0,
|
[SettingKeyboard.Alt_Button_Cycle_Nature]: 0,
|
||||||
[SettingKeyboard.Button_Cycle_Variant]: 0,
|
[SettingKeyboard.Button_Cycle_Tera]: 0,
|
||||||
[SettingKeyboard.Alt_Button_Cycle_Variant]: 0,
|
[SettingKeyboard.Alt_Button_Cycle_Tera]: 0,
|
||||||
[SettingKeyboard.Button_Speed_Up]: 0,
|
[SettingKeyboard.Button_Speed_Up]: 0,
|
||||||
[SettingKeyboard.Alt_Button_Speed_Up]: 0,
|
[SettingKeyboard.Alt_Button_Speed_Up]: 0,
|
||||||
[SettingKeyboard.Button_Slow_Down]: 0,
|
[SettingKeyboard.Button_Slow_Down]: 0,
|
||||||
@ -148,7 +148,7 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo
|
|||||||
case SettingKeyboard.Button_Cycle_Gender:
|
case SettingKeyboard.Button_Cycle_Gender:
|
||||||
case SettingKeyboard.Button_Cycle_Ability:
|
case SettingKeyboard.Button_Cycle_Ability:
|
||||||
case SettingKeyboard.Button_Cycle_Nature:
|
case SettingKeyboard.Button_Cycle_Nature:
|
||||||
case SettingKeyboard.Button_Cycle_Variant:
|
case SettingKeyboard.Button_Cycle_Tera:
|
||||||
case SettingKeyboard.Button_Speed_Up:
|
case SettingKeyboard.Button_Speed_Up:
|
||||||
case SettingKeyboard.Button_Slow_Down:
|
case SettingKeyboard.Button_Slow_Down:
|
||||||
case SettingKeyboard.Alt_Button_Up:
|
case SettingKeyboard.Alt_Button_Up:
|
||||||
@ -164,7 +164,7 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo
|
|||||||
case SettingKeyboard.Alt_Button_Cycle_Gender:
|
case SettingKeyboard.Alt_Button_Cycle_Gender:
|
||||||
case SettingKeyboard.Alt_Button_Cycle_Ability:
|
case SettingKeyboard.Alt_Button_Cycle_Ability:
|
||||||
case SettingKeyboard.Alt_Button_Cycle_Nature:
|
case SettingKeyboard.Alt_Button_Cycle_Nature:
|
||||||
case SettingKeyboard.Alt_Button_Cycle_Variant:
|
case SettingKeyboard.Alt_Button_Cycle_Tera:
|
||||||
case SettingKeyboard.Alt_Button_Speed_Up:
|
case SettingKeyboard.Alt_Button_Speed_Up:
|
||||||
case SettingKeyboard.Alt_Button_Slow_Down:
|
case SettingKeyboard.Alt_Button_Slow_Down:
|
||||||
case SettingKeyboard.Alt_Button_Submit:
|
case SettingKeyboard.Alt_Button_Submit:
|
||||||
|
@ -391,7 +391,7 @@ describe("Abilities - Unburden", () => {
|
|||||||
await game.forceEnemyMove(Moves.THIEF, BattlerIndex.PLAYER);
|
await game.forceEnemyMove(Moves.THIEF, BattlerIndex.PLAYER);
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2 ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2 ]);
|
||||||
game.doSelectPartyPokemon(0, "MoveEffectPhase");
|
game.doSelectPartyPokemon(0, "RevivalBlessingPhase");
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(game.scene.getPlayerField()[0]).toBe(treecko);
|
expect(game.scene.getPlayerField()[0]).toBe(treecko);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
|
import type { EnemyPersistentModifier } from "#app/modifier/modifier";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
@ -65,6 +67,28 @@ describe("Battle Mechanics - Damage Calculation", () => {
|
|||||||
expect(aggron.hp).toBe(aggron.getMaxHp() - 1);
|
expect(aggron.hp).toBe(aggron.getMaxHp() - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Attacks deal 1 damage at minimum even with many tokens", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(1)
|
||||||
|
.enemySpecies(Species.AGGRON)
|
||||||
|
.enemyAbility(Abilities.STURDY)
|
||||||
|
.enemyLevel(10000);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([ Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const dmg_redux_modifier = modifierTypes.ENEMY_DAMAGE_REDUCTION().newModifier() as EnemyPersistentModifier;
|
||||||
|
dmg_redux_modifier.stackCount = 1000;
|
||||||
|
await game.scene.addEnemyModifier(modifierTypes.ENEMY_DAMAGE_REDUCTION().newModifier() as EnemyPersistentModifier);
|
||||||
|
|
||||||
|
const aggron = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.TACKLE);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(aggron.hp).toBe(aggron.getMaxHp() - 1);
|
||||||
|
});
|
||||||
|
|
||||||
it("Fixed-damage moves ignore damage multipliers", async () => {
|
it("Fixed-damage moves ignore damage multipliers", async () => {
|
||||||
game.override
|
game.override
|
||||||
.enemySpecies(Species.DRAGONITE)
|
.enemySpecies(Species.DRAGONITE)
|
||||||
|
99
src/test/moves/quash.test.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { describe, beforeAll, afterEach, beforeEach, it, expect } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Quash", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyLevel(1)
|
||||||
|
.enemySpecies(Species.SLOWPOKE)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset([ Moves.RAIN_DANCE, Moves.SPLASH ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.moveset([ Moves.QUASH, Moves.SUNNY_DAY, Moves.RAIN_DANCE, Moves.SPLASH ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("makes the target move last in a turn, ignoring priority", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.ACCELGOR, Species.RATTATA ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.QUASH, 0, BattlerIndex.PLAYER_2);
|
||||||
|
game.move.select(Moves.SUNNY_DAY, 1);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
await game.forceEnemyMove(Moves.RAIN_DANCE);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase", false);
|
||||||
|
// will be sunny if player_2 moved last because of quash, rainy otherwise
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SUNNY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if the target has already moved", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.ACCELGOR, Species.RATTATA ]);
|
||||||
|
game.move.select(Moves.SPLASH, 0);
|
||||||
|
game.move.select(Moves.QUASH, 1, BattlerIndex.PLAYER);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("makes multiple quashed targets move in speed order at the end of the turn", async () => {
|
||||||
|
game.override.enemySpecies(Species.NINJASK)
|
||||||
|
.enemyLevel(100);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([ Species.ACCELGOR, Species.RATTATA ]);
|
||||||
|
|
||||||
|
// both users are quashed - rattata is slower so sun should be up at end of turn
|
||||||
|
game.move.select(Moves.RAIN_DANCE, 0);
|
||||||
|
game.move.select(Moves.SUNNY_DAY, 1);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.QUASH, BattlerIndex.PLAYER);
|
||||||
|
await game.forceEnemyMove(Moves.QUASH, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase", false);
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SUNNY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects trick room", async () => {
|
||||||
|
game.override.enemyMoveset([ Moves.RAIN_DANCE, Moves.SPLASH, Moves.TRICK_ROOM ]);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([ Species.ACCELGOR, Species.RATTATA ]);
|
||||||
|
game.move.select(Moves.SPLASH, 0);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.TRICK_ROOM);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnInitPhase");
|
||||||
|
// both users are quashed - accelgor should move last w/ TR so rain should be up at end of turn
|
||||||
|
game.move.select(Moves.RAIN_DANCE, 0);
|
||||||
|
game.move.select(Moves.SUNNY_DAY, 1);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.QUASH, BattlerIndex.PLAYER);
|
||||||
|
await game.forceEnemyMove(Moves.QUASH, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase", false);
|
||||||
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.RAIN);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
117
src/test/moves/revival_blessing.test.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { toDmgValue } from "#app/utils";
|
||||||
|
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 - Revival Blessing", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SPLASH, Moves.REVIVAL_BLESSING, Moves.MEMENTO ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should revive a selected fainted Pokemon when used by the player", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MEMENTO);
|
||||||
|
game.doSelectPartyPokemon(1, "SwitchPhase");
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
expect(player.species.speciesId).toBe(Species.MAGIKARP);
|
||||||
|
game.move.select(Moves.REVIVAL_BLESSING);
|
||||||
|
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
game.doSelectPartyPokemon(1, "RevivalBlessingPhase");
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase", false);
|
||||||
|
|
||||||
|
const revivedPokemon = game.scene.getPlayerParty()[1];
|
||||||
|
expect(revivedPokemon.status?.effect).toBeFalsy();
|
||||||
|
expect(revivedPokemon.hp).toBe(Math.floor(revivedPokemon.getMaxHp() / 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should revive a random fainted enemy when used by an enemy Trainer", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.REVIVAL_BLESSING).startingWave(8);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.doKillOpponents();
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase", false);
|
||||||
|
|
||||||
|
const revivedPokemon = game.scene.getEnemyParty()[1];
|
||||||
|
expect(revivedPokemon.status?.effect).toBeFalsy();
|
||||||
|
expect(revivedPokemon.hp).toBe(Math.floor(revivedPokemon.getMaxHp() / 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when there are no fainted Pokemon to target", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.REVIVAL_BLESSING);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase", false);
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(player.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should revive a player pokemon and immediately send it back out if used in the same turn it fainted in doubles", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyMoveset([ Moves.SPLASH, Moves.FISSURE ])
|
||||||
|
.enemyAbility(Abilities.NO_GUARD)
|
||||||
|
.enemyLevel(100);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC, Species.GYARADOS ]);
|
||||||
|
|
||||||
|
const feebas = game.scene.getPlayerField()[0];
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.move.select(Moves.REVIVAL_BLESSING, 1);
|
||||||
|
await game.forceEnemyMove(Moves.FISSURE, BattlerIndex.PLAYER);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2 ]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
|
||||||
|
expect(feebas.isFainted()).toBe(true);
|
||||||
|
|
||||||
|
game.doSelectPartyPokemon(0, "RevivalBlessingPhase");
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(feebas.isFainted()).toBe(false);
|
||||||
|
expect(feebas.hp).toBe(toDmgValue(0.5 * feebas.getMaxHp()));
|
||||||
|
expect(game.scene.getPlayerField()[0]).toBe(feebas);
|
||||||
|
});
|
||||||
|
});
|
@ -375,6 +375,6 @@ describe("Clowning Around - Mystery Encounter", () => {
|
|||||||
async function addItemToPokemon(scene: BattleScene, pokemon: Pokemon, stackCount: number, itemType: PokemonHeldItemModifierType) {
|
async function addItemToPokemon(scene: BattleScene, pokemon: Pokemon, stackCount: number, itemType: PokemonHeldItemModifierType) {
|
||||||
const itemMod = itemType.newModifier(pokemon) as PokemonHeldItemModifier;
|
const itemMod = itemType.newModifier(pokemon) as PokemonHeldItemModifier;
|
||||||
itemMod.stackCount = stackCount;
|
itemMod.stackCount = stackCount;
|
||||||
await scene.addModifier(itemMod, true, false, false, true);
|
scene.addModifier(itemMod, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
}
|
}
|
||||||
|
@ -123,8 +123,6 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
|||||||
partyLead.level = 1000;
|
partyLead.level = 1000;
|
||||||
partyLead.calculateStats();
|
partyLead.calculateStats();
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
// For some reason updateModifiers breaks in this test and does not resolve promise
|
|
||||||
vi.spyOn(game.scene, "updateModifiers").mockImplementation(() => new Promise(resolve => resolve()));
|
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
@ -123,7 +123,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const amuletCoin = generateModifierType(modifierTypes.AMULET_COIN)!.newModifier() as MoneyMultiplierModifier;
|
const amuletCoin = generateModifierType(modifierTypes.AMULET_COIN)!.newModifier() as MoneyMultiplierModifier;
|
||||||
amuletCoin.stackCount = 5;
|
amuletCoin.stackCount = 5;
|
||||||
await scene.addModifier(amuletCoin, true, false, false, true);
|
scene.addModifier(amuletCoin, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
@ -193,7 +193,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
||||||
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
||||||
sitrusMod.stackCount = 2;
|
sitrusMod.stackCount = 2;
|
||||||
await scene.addModifier(sitrusMod, true, false, false, true);
|
scene.addModifier(sitrusMod, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -214,7 +214,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
||||||
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -234,13 +234,13 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const candyJar = generateModifierType(modifierTypes.CANDY_JAR)!.newModifier() as LevelIncrementBoosterModifier;
|
const candyJar = generateModifierType(modifierTypes.CANDY_JAR)!.newModifier() as LevelIncrementBoosterModifier;
|
||||||
candyJar.stackCount = 99;
|
candyJar.stackCount = 99;
|
||||||
await scene.addModifier(candyJar, true, false, false, true);
|
scene.addModifier(candyJar, true, false, false, true);
|
||||||
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
||||||
|
|
||||||
// Sitrus berries on party
|
// Sitrus berries on party
|
||||||
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
||||||
sitrusMod.stackCount = 2;
|
sitrusMod.stackCount = 2;
|
||||||
await scene.addModifier(sitrusMod, true, false, false, true);
|
scene.addModifier(sitrusMod, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -263,13 +263,13 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const healingCharm = generateModifierType(modifierTypes.BERRY_POUCH)!.newModifier() as PreserveBerryModifier;
|
const healingCharm = generateModifierType(modifierTypes.BERRY_POUCH)!.newModifier() as PreserveBerryModifier;
|
||||||
healingCharm.stackCount = 3;
|
healingCharm.stackCount = 3;
|
||||||
await scene.addModifier(healingCharm, true, false, false, true);
|
scene.addModifier(healingCharm, true, false, false, true);
|
||||||
|
|
||||||
// Set 1 Reviver Seed on party lead
|
// Set 1 Reviver Seed on party lead
|
||||||
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
||||||
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -292,7 +292,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]);
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]);
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
@ -321,7 +321,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
||||||
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -355,7 +355,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 2;
|
modifier.stackCount = 2;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -376,7 +376,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -396,13 +396,13 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const healingCharm = generateModifierType(modifierTypes.HEALING_CHARM)!.newModifier() as HealingBoosterModifier;
|
const healingCharm = generateModifierType(modifierTypes.HEALING_CHARM)!.newModifier() as HealingBoosterModifier;
|
||||||
healingCharm.stackCount = 5;
|
healingCharm.stackCount = 5;
|
||||||
await scene.addModifier(healingCharm, true, false, false, true);
|
scene.addModifier(healingCharm, true, false, false, true);
|
||||||
|
|
||||||
// Set 1 Soul Dew on party lead
|
// Set 1 Soul Dew on party lead
|
||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -425,7 +425,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
scene.modifiers = [];
|
scene.modifiers = [];
|
||||||
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
|
||||||
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]);
|
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]);
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||||
@ -455,7 +455,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
|||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
|
@ -224,7 +224,7 @@ describe("Global Trade System - Mystery Encounter", () => {
|
|||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 2;
|
modifier.stackCount = 2;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
@ -249,7 +249,7 @@ describe("Global Trade System - Mystery Encounter", () => {
|
|||||||
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
|
||||||
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier;
|
||||||
modifier.stackCount = 1;
|
modifier.stackCount = 1;
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
scene.addModifier(modifier, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
|
@ -216,11 +216,11 @@ describe("Uncommon Breed - Mystery Encounter", () => {
|
|||||||
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
const sitrus = generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ])!;
|
||||||
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
||||||
sitrusMod.stackCount = 2;
|
sitrusMod.stackCount = 2;
|
||||||
await scene.addModifier(sitrusMod, true, false, false, true);
|
scene.addModifier(sitrusMod, true, false, false, true);
|
||||||
const ganlon = generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ])!;
|
const ganlon = generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ])!;
|
||||||
const ganlonMod = ganlon.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
const ganlonMod = ganlon.newModifier(scene.getPlayerParty()[0]) as BerryModifier;
|
||||||
ganlonMod.stackCount = 3;
|
ganlonMod.stackCount = 3;
|
||||||
await scene.addModifier(ganlonMod, true, false, false, true);
|
scene.addModifier(ganlonMod, true, false, false, true);
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
@ -406,9 +406,9 @@ describe("Test Rebinding", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("check to delete all the binds of an action", () => {
|
it("check to delete all the binds of an action", () => {
|
||||||
inGame.whenWePressOnKeyboard("V").weShouldTriggerTheButton("Button_Cycle_Variant");
|
inGame.whenWePressOnKeyboard("V").weShouldTriggerTheButton("Button_Cycle_Tera");
|
||||||
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Variant").thereShouldBeNoIcon().weWantThisBindInstead("K").confirm();
|
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Tera").thereShouldBeNoIcon().weWantThisBindInstead("K").confirm();
|
||||||
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Variant").iconDisplayedIs("KEY_K").whenWeDelete().thereShouldBeNoIconAnymore();
|
inTheSettingMenu.whenCursorIsOnSetting("Alt_Button_Cycle_Tera").iconDisplayedIs("KEY_K").whenWeDelete().thereShouldBeNoIconAnymore();
|
||||||
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Variant").iconDisplayedIs("KEY_V").whenWeDelete().thereShouldBeNoIconAnymore();
|
inTheSettingMenu.whenCursorIsOnSetting("Button_Cycle_Tera").iconDisplayedIs("KEY_V").whenWeDelete().thereShouldBeNoIconAnymore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<div id="apadCycleShiny" class="apad-button apad-square apad-small" data-key="CYCLE_SHINY">
|
<div id="apadCycleShiny" class="apad-button apad-square apad-small" data-key="CYCLE_SHINY">
|
||||||
<span class="apad-label">R</span>
|
<span class="apad-label">R</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="apadCycleVariant" class="apad-button apad-square apad-small" data-key="V">
|
<div id="apadCycleTera" class="apad-button apad-square apad-small" data-key="CYCLE_TERA">
|
||||||
<span class="apad-label">V</span>
|
<span class="apad-label">V</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="apadStats" class="apad-button apad-rectangle apad-small" data-key="STATS">
|
<div id="apadStats" class="apad-button apad-rectangle apad-small" data-key="STATS">
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<div id="apadCycleNature" class="apad-button apad-square apad-small" data-key="CYCLE_NATURE">
|
<div id="apadCycleNature" class="apad-button apad-square apad-small" data-key="CYCLE_NATURE">
|
||||||
<span class="apad-label">N</span>
|
<span class="apad-label">N</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="apadInfo" class="apad-button apad-rectangle apad-small" data-key="V">
|
<div id="apadInfo" class="apad-button apad-rectangle apad-small" data-key="CYCLE_TERA">
|
||||||
<span class="apad-label">V</span>
|
<span class="apad-label">V</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,6 +91,7 @@ export default class GameWrapper {
|
|||||||
Pokemon.prototype.updateFusionPalette = () => null;
|
Pokemon.prototype.updateFusionPalette = () => null;
|
||||||
Pokemon.prototype.cry = () => null;
|
Pokemon.prototype.cry = () => null;
|
||||||
Pokemon.prototype.faintCry = (cb) => { if (cb) cb(); };
|
Pokemon.prototype.faintCry = (cb) => { if (cb) cb(); };
|
||||||
|
BattleScene.prototype.addPokemonIcon = () => new Phaser.GameObjects.Container(this.scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
setScene(scene: BattleScene) {
|
setScene(scene: BattleScene) {
|
||||||
|
@ -60,6 +60,7 @@ import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-ph
|
|||||||
import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase";
|
import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase";
|
||||||
import { UnlockPhase } from "#app/phases/unlock-phase";
|
import { UnlockPhase } from "#app/phases/unlock-phase";
|
||||||
import { PostGameOverPhase } from "#app/phases/post-game-over-phase";
|
import { PostGameOverPhase } from "#app/phases/post-game-over-phase";
|
||||||
|
import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase";
|
||||||
|
|
||||||
export interface PromptHandler {
|
export interface PromptHandler {
|
||||||
phaseTarget?: string;
|
phaseTarget?: string;
|
||||||
@ -126,7 +127,8 @@ type PhaseClass =
|
|||||||
| typeof EncounterPhase
|
| typeof EncounterPhase
|
||||||
| typeof GameOverPhase
|
| typeof GameOverPhase
|
||||||
| typeof UnlockPhase
|
| typeof UnlockPhase
|
||||||
| typeof PostGameOverPhase;
|
| typeof PostGameOverPhase
|
||||||
|
| typeof RevivalBlessingPhase;
|
||||||
|
|
||||||
type PhaseString =
|
type PhaseString =
|
||||||
| "LoginPhase"
|
| "LoginPhase"
|
||||||
@ -185,7 +187,8 @@ type PhaseString =
|
|||||||
| "EncounterPhase"
|
| "EncounterPhase"
|
||||||
| "GameOverPhase"
|
| "GameOverPhase"
|
||||||
| "UnlockPhase"
|
| "UnlockPhase"
|
||||||
| "PostGameOverPhase";
|
| "PostGameOverPhase"
|
||||||
|
| "RevivalBlessingPhase";
|
||||||
|
|
||||||
type PhaseInterceptorPhase = PhaseClass | PhaseString;
|
type PhaseInterceptorPhase = PhaseClass | PhaseString;
|
||||||
|
|
||||||
@ -269,6 +272,7 @@ export default class PhaseInterceptor {
|
|||||||
[ GameOverPhase, this.startPhase ],
|
[ GameOverPhase, this.startPhase ],
|
||||||
[ UnlockPhase, this.startPhase ],
|
[ UnlockPhase, this.startPhase ],
|
||||||
[ PostGameOverPhase, this.startPhase ],
|
[ PostGameOverPhase, this.startPhase ],
|
||||||
|
[ RevivalBlessingPhase, this.startPhase ],
|
||||||
];
|
];
|
||||||
|
|
||||||
private endBySetMode = [
|
private endBySetMode = [
|
||||||
@ -511,11 +515,11 @@ export default class PhaseInterceptor {
|
|||||||
if (expireFn) {
|
if (expireFn) {
|
||||||
this.prompts.shift();
|
this.prompts.shift();
|
||||||
} else if (
|
} else if (
|
||||||
currentMode === actionForNextPrompt.mode
|
currentMode === actionForNextPrompt.mode &&
|
||||||
&& currentPhase === actionForNextPrompt.phaseTarget
|
currentPhase === actionForNextPrompt.phaseTarget &&
|
||||||
&& currentHandler.active
|
currentHandler.active &&
|
||||||
&& (!actionForNextPrompt.awaitingActionInput
|
(!actionForNextPrompt.awaitingActionInput ||
|
||||||
|| (actionForNextPrompt.awaitingActionInput && currentHandler.awaitingActionInput))
|
(actionForNextPrompt.awaitingActionInput && currentHandler.awaitingActionInput))
|
||||||
) {
|
) {
|
||||||
const prompt = this.prompts.shift();
|
const prompt = this.prompts.shift();
|
||||||
if (prompt?.callback) {
|
if (prompt?.callback) {
|
||||||
|
@ -86,7 +86,7 @@ export class UiInputs {
|
|||||||
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
|
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
|
||||||
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
|
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
|
||||||
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
|
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
|
||||||
[Button.V]: () => this.buttonCycleOption(Button.V),
|
[Button.CYCLE_TERA]: () => this.buttonCycleOption(Button.CYCLE_TERA),
|
||||||
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
|
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
|
||||||
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
|
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
|
||||||
};
|
};
|
||||||
@ -109,7 +109,7 @@ export class UiInputs {
|
|||||||
[Button.CYCLE_GENDER]: () => undefined,
|
[Button.CYCLE_GENDER]: () => undefined,
|
||||||
[Button.CYCLE_ABILITY]: () => undefined,
|
[Button.CYCLE_ABILITY]: () => undefined,
|
||||||
[Button.CYCLE_NATURE]: () => undefined,
|
[Button.CYCLE_NATURE]: () => undefined,
|
||||||
[Button.V]: () => this.buttonInfo(false),
|
[Button.CYCLE_TERA]: () => undefined,
|
||||||
[Button.SPEED_UP]: () => undefined,
|
[Button.SPEED_UP]: () => undefined,
|
||||||
[Button.SLOW_DOWN]: () => undefined,
|
[Button.SLOW_DOWN]: () => undefined,
|
||||||
};
|
};
|
||||||
@ -197,7 +197,7 @@ export class UiInputs {
|
|||||||
const uiHandler = globalScene.ui?.getHandler();
|
const uiHandler = globalScene.ui?.getHandler();
|
||||||
if (whitelist.some(handler => uiHandler instanceof handler)) {
|
if (whitelist.some(handler => uiHandler instanceof handler)) {
|
||||||
globalScene.ui.processInput(button);
|
globalScene.ui.processInput(button);
|
||||||
} else if (button === Button.V) {
|
} else if (button === Button.CYCLE_TERA) {
|
||||||
this.buttonInfo(true);
|
this.buttonInfo(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { TextStyle, addTextObject } from "./text";
|
import { TextStyle, addTextObject } from "./text";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
export default class PartyExpBar extends Phaser.GameObjects.Container {
|
export default class PartyExpBar extends Phaser.GameObjects.Container {
|
||||||
private bg: Phaser.GameObjects.NineSlice;
|
private bg: Phaser.GameObjects.NineSlice;
|
||||||
@ -43,9 +44,9 @@ export default class PartyExpBar extends Phaser.GameObjects.Container {
|
|||||||
// if we want to only display the level in the small frame
|
// if we want to only display the level in the small frame
|
||||||
if (showOnlyLevelUp) {
|
if (showOnlyLevelUp) {
|
||||||
if (newLevel > 200) { // if the level is greater than 200, we only display Lv. UP
|
if (newLevel > 200) { // if the level is greater than 200, we only display Lv. UP
|
||||||
this.expText.setText("Lv. UP");
|
this.expText.setText(i18next.t("battleScene:levelUp"));
|
||||||
} else { // otherwise we display Lv. Up and the new level
|
} else { // otherwise we display Lv. Up and the new level
|
||||||
this.expText.setText(`Lv. UP: ${newLevel.toString()}`);
|
this.expText.setText(i18next.t("battleScene:levelUpWithLevel", { level: newLevel }));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if we want to display the exp
|
// if we want to display the exp
|
||||||
|
@ -45,7 +45,6 @@ import { EggSourceType } from "#enums/egg-source-types";
|
|||||||
import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCandyCounts } from "#app/data/balance/starters";
|
import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCandyCounts } from "#app/data/balance/starters";
|
||||||
import { BooleanHolder, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, rgbHexToRgba, toReadableString } from "#app/utils";
|
import { BooleanHolder, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, rgbHexToRgba, toReadableString } from "#app/utils";
|
||||||
import type { Nature } from "#enums/nature";
|
import type { Nature } from "#enums/nature";
|
||||||
import BgmBar from "./bgm-bar";
|
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { speciesTmMoves } from "#app/data/balance/tms";
|
import { speciesTmMoves } from "#app/data/balance/tms";
|
||||||
import type { BiomeTierTod } from "#app/data/balance/biomes";
|
import type { BiomeTierTod } from "#app/data/balance/biomes";
|
||||||
@ -242,7 +241,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||||||
private menuContainer: Phaser.GameObjects.Container;
|
private menuContainer: Phaser.GameObjects.Container;
|
||||||
private menuBg: Phaser.GameObjects.NineSlice;
|
private menuBg: Phaser.GameObjects.NineSlice;
|
||||||
protected optionSelectText: Phaser.GameObjects.Text;
|
protected optionSelectText: Phaser.GameObjects.Text;
|
||||||
public bgmBar: BgmBar;
|
|
||||||
private menuOptions: MenuOptions[];
|
private menuOptions: MenuOptions[];
|
||||||
protected scale: number = 0.1666666667;
|
protected scale: number = 0.1666666667;
|
||||||
private menuDescriptions: string[];
|
private menuDescriptions: string[];
|
||||||
@ -480,10 +478,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||||||
this.menuContainer.setName("menu");
|
this.menuContainer.setName("menu");
|
||||||
this.menuContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
|
this.menuContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
|
||||||
|
|
||||||
this.bgmBar = new BgmBar();
|
|
||||||
this.bgmBar.setup();
|
|
||||||
ui.bgmBar = this.bgmBar;
|
|
||||||
this.menuContainer.add(this.bgmBar);
|
|
||||||
this.menuContainer.setVisible(false);
|
this.menuContainer.setVisible(false);
|
||||||
|
|
||||||
this.menuOptions = Utils.getEnumKeys(MenuOptions).map(m => parseInt(MenuOptions[m]) as MenuOptions);
|
this.menuOptions = Utils.getEnumKeys(MenuOptions).map(m => parseInt(MenuOptions[m]) as MenuOptions);
|
||||||
@ -983,13 +977,23 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
||||||
options: this.levelMoves.map(m => {
|
options: this.levelMoves.map(m => {
|
||||||
|
const levelNumber = m[0] > 0 ? String(m[0]) : "";
|
||||||
const option: OptionSelectItem = {
|
const option: OptionSelectItem = {
|
||||||
label: String(m[0]).padEnd(4, " ") + allMoves[m[1]].name,
|
label: levelNumber.padEnd(4, " ") + allMoves[m[1]].name,
|
||||||
handler: () => {
|
handler: () => {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
onHover: () => {
|
onHover: () => {
|
||||||
this.moveInfoOverlay.show(allMoves[m[1]]);
|
this.moveInfoOverlay.show(allMoves[m[1]]);
|
||||||
|
if (m[0] === 0) {
|
||||||
|
this.showText(i18next.t("pokedexUiHandler:onlyEvolutionMove"));
|
||||||
|
} else if (m[0] === -1) {
|
||||||
|
this.showText(i18next.t("pokedexUiHandler:onlyRecallMove"));
|
||||||
|
} else if (m[0] <= 5) {
|
||||||
|
this.showText(i18next.t("pokedexUiHandler:onStarterSelectMove"));
|
||||||
|
} else {
|
||||||
|
this.showText(i18next.t("pokedexUiHandler:byLevelUpMove"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return option;
|
return option;
|
||||||
|
@ -919,7 +919,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
|
|||||||
} else {
|
} else {
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
} else if (button === Button.V) {
|
} else if (button === Button.CYCLE_TERA) {
|
||||||
if (!this.filterTextMode && !this.showingTray) {
|
if (!this.filterTextMode && !this.showingTray) {
|
||||||
this.cursorObj.setVisible(false);
|
this.cursorObj.setVisible(false);
|
||||||
this.setSpecies(null);
|
this.setSpecies(null);
|
||||||
@ -1170,9 +1170,6 @@ export default class PokedexUiHandler extends MessageUiHandler {
|
|||||||
case SettingKeyboard.Button_Cycle_Shiny:
|
case SettingKeyboard.Button_Cycle_Shiny:
|
||||||
iconPath = "R.png";
|
iconPath = "R.png";
|
||||||
break;
|
break;
|
||||||
case SettingKeyboard.Button_Cycle_Variant:
|
|
||||||
iconPath = "V.png";
|
|
||||||
break;
|
|
||||||
case SettingKeyboard.Button_Cycle_Form:
|
case SettingKeyboard.Button_Cycle_Form:
|
||||||
iconPath = "F.png";
|
iconPath = "F.png";
|
||||||
break;
|
break;
|
||||||
|
@ -93,9 +93,9 @@ export default class MoveTouchControlsHandler {
|
|||||||
toolbar.innerHTML = `
|
toolbar.innerHTML = `
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
<div id="resetButton" class="button">${i18next.t("settings:reset")}</div>
|
<div id="resetButton" class="button">${i18next.t("settings:touchReset")}</div>
|
||||||
<div id="saveButton" class="button">${i18next.t("settings:saveClose")}</div>
|
<div id="saveButton" class="button">${i18next.t("settings:touchSaveClose")}</div>
|
||||||
<div id="cancelButton" class="button">${i18next.t("settings:cancel")}</div>
|
<div id="cancelButton" class="button">${i18next.t("settings:touchCancel")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="orientation-label">
|
<div class="orientation-label">
|
||||||
|
@ -56,6 +56,8 @@ import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCa
|
|||||||
import { BooleanHolder, fixedInt, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, randIntRange, rgbHexToRgba, toReadableString } from "#app/utils";
|
import { BooleanHolder, fixedInt, getLocalizedSpriteKey, isNullOrUndefined, NumberHolder, padInt, randIntRange, rgbHexToRgba, toReadableString } from "#app/utils";
|
||||||
import type { Nature } from "#enums/nature";
|
import type { Nature } from "#enums/nature";
|
||||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
|
import { achvs } from "#app/system/achv";
|
||||||
|
import * as Utils from "../utils";
|
||||||
|
|
||||||
export type StarterSelectCallback = (starters: Starter[]) => void;
|
export type StarterSelectCallback = (starters: Starter[]) => void;
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ export interface Starter {
|
|||||||
moveset?: StarterMoveset;
|
moveset?: StarterMoveset;
|
||||||
pokerus: boolean;
|
pokerus: boolean;
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
|
teraType?: Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LanguageSetting {
|
interface LanguageSetting {
|
||||||
@ -212,6 +215,7 @@ interface SpeciesDetails {
|
|||||||
abilityIndex?: number,
|
abilityIndex?: number,
|
||||||
natureIndex?: number,
|
natureIndex?: number,
|
||||||
forSeen?: boolean, // default = false
|
forSeen?: boolean, // default = false
|
||||||
|
teraType?: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class StarterSelectUiHandler extends MessageUiHandler {
|
export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
@ -262,6 +266,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
private pokemonShinyIcon: Phaser.GameObjects.Sprite;
|
private pokemonShinyIcon: Phaser.GameObjects.Sprite;
|
||||||
private pokemonPassiveDisabledIcon: Phaser.GameObjects.Sprite;
|
private pokemonPassiveDisabledIcon: Phaser.GameObjects.Sprite;
|
||||||
private pokemonPassiveLockedIcon: Phaser.GameObjects.Sprite;
|
private pokemonPassiveLockedIcon: Phaser.GameObjects.Sprite;
|
||||||
|
private teraIcon: Phaser.GameObjects.Sprite;
|
||||||
|
|
||||||
private activeTooltip: "ABILITY" | "PASSIVE" | "CANDY" | undefined;
|
private activeTooltip: "ABILITY" | "PASSIVE" | "CANDY" | undefined;
|
||||||
private instructionsContainer: Phaser.GameObjects.Container;
|
private instructionsContainer: Phaser.GameObjects.Container;
|
||||||
@ -271,12 +276,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
private abilityIconElement: Phaser.GameObjects.Sprite;
|
private abilityIconElement: Phaser.GameObjects.Sprite;
|
||||||
private genderIconElement: Phaser.GameObjects.Sprite;
|
private genderIconElement: Phaser.GameObjects.Sprite;
|
||||||
private natureIconElement: Phaser.GameObjects.Sprite;
|
private natureIconElement: Phaser.GameObjects.Sprite;
|
||||||
|
private teraIconElement: Phaser.GameObjects.Sprite;
|
||||||
private goFilterIconElement: Phaser.GameObjects.Sprite;
|
private goFilterIconElement: Phaser.GameObjects.Sprite;
|
||||||
private shinyLabel: Phaser.GameObjects.Text;
|
private shinyLabel: Phaser.GameObjects.Text;
|
||||||
private formLabel: Phaser.GameObjects.Text;
|
private formLabel: Phaser.GameObjects.Text;
|
||||||
private genderLabel: Phaser.GameObjects.Text;
|
private genderLabel: Phaser.GameObjects.Text;
|
||||||
private abilityLabel: Phaser.GameObjects.Text;
|
private abilityLabel: Phaser.GameObjects.Text;
|
||||||
private natureLabel: Phaser.GameObjects.Text;
|
private natureLabel: Phaser.GameObjects.Text;
|
||||||
|
private teraLabel: Phaser.GameObjects.Text;
|
||||||
private goFilterLabel: Phaser.GameObjects.Text;
|
private goFilterLabel: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
private starterSelectMessageBox: Phaser.GameObjects.NineSlice;
|
private starterSelectMessageBox: Phaser.GameObjects.NineSlice;
|
||||||
@ -292,6 +299,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
private dexAttrCursor: bigint = 0n;
|
private dexAttrCursor: bigint = 0n;
|
||||||
private abilityCursor: number = -1;
|
private abilityCursor: number = -1;
|
||||||
private natureCursor: number = -1;
|
private natureCursor: number = -1;
|
||||||
|
private teraCursor: Type = Type.UNKNOWN;
|
||||||
private filterBarCursor: number = 0;
|
private filterBarCursor: number = 0;
|
||||||
private starterMoveset: StarterMoveset | null;
|
private starterMoveset: StarterMoveset | null;
|
||||||
private scrollCursor: number;
|
private scrollCursor: number;
|
||||||
@ -304,6 +312,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
private starterAttr: bigint[] = [];
|
private starterAttr: bigint[] = [];
|
||||||
private starterAbilityIndexes: number[] = [];
|
private starterAbilityIndexes: number[] = [];
|
||||||
private starterNatures: Nature[] = [];
|
private starterNatures: Nature[] = [];
|
||||||
|
private starterTeras: Type[] = [];
|
||||||
private starterMovesets: StarterMoveset[] = [];
|
private starterMovesets: StarterMoveset[] = [];
|
||||||
private speciesStarterDexEntry: DexEntry | null;
|
private speciesStarterDexEntry: DexEntry | null;
|
||||||
private speciesStarterMoves: Moves[];
|
private speciesStarterMoves: Moves[];
|
||||||
@ -312,6 +321,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
private canCycleGender: boolean;
|
private canCycleGender: boolean;
|
||||||
private canCycleAbility: boolean;
|
private canCycleAbility: boolean;
|
||||||
private canCycleNature: boolean;
|
private canCycleNature: boolean;
|
||||||
|
private canCycleTera: boolean;
|
||||||
|
|
||||||
private assetLoadCancelled: BooleanHolder | null;
|
private assetLoadCancelled: BooleanHolder | null;
|
||||||
public cursorObj: Phaser.GameObjects.Image;
|
public cursorObj: Phaser.GameObjects.Image;
|
||||||
@ -823,6 +833,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.starterSelectContainer.add(this.pokemonEggMovesContainer);
|
this.starterSelectContainer.add(this.pokemonEggMovesContainer);
|
||||||
|
|
||||||
|
this.teraIcon = globalScene.add.sprite(85, 63, "button_tera");
|
||||||
|
this.teraIcon.setName("terrastallize-icon");
|
||||||
|
this.teraIcon.setFrame("fire");
|
||||||
|
this.starterSelectContainer.add(this.teraIcon);
|
||||||
|
|
||||||
// The font size should be set per language
|
// The font size should be set per language
|
||||||
const instructionTextSize = textSettings.instructionTextSize;
|
const instructionTextSize = textSettings.instructionTextSize;
|
||||||
|
|
||||||
@ -867,6 +882,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.natureLabel = addTextObject(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleNature"), TextStyle.PARTY, { fontSize: instructionTextSize });
|
this.natureLabel = addTextObject(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleNature"), TextStyle.PARTY, { fontSize: instructionTextSize });
|
||||||
this.natureLabel.setName("text-nature-label");
|
this.natureLabel.setName("text-nature-label");
|
||||||
|
|
||||||
|
this.teraIconElement = new Phaser.GameObjects.Sprite(globalScene, this.instructionRowX, this.instructionRowY, "keyboard", "V.png");
|
||||||
|
this.teraIconElement.setName("sprite-tera-icon-element");
|
||||||
|
this.teraIconElement.setScale(0.675);
|
||||||
|
this.teraIconElement.setOrigin(0.0, 0.0);
|
||||||
|
this.teraLabel = addTextObject(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleTera"), TextStyle.PARTY, { fontSize: instructionTextSize });
|
||||||
|
this.teraLabel.setName("text-tera-label");
|
||||||
|
|
||||||
this.goFilterIconElement = new Phaser.GameObjects.Sprite(globalScene, this.filterInstructionRowX, this.filterInstructionRowY, "keyboard", "C.png");
|
this.goFilterIconElement = new Phaser.GameObjects.Sprite(globalScene, this.filterInstructionRowX, this.filterInstructionRowY, "keyboard", "C.png");
|
||||||
this.goFilterIconElement.setName("sprite-goFilter-icon-element");
|
this.goFilterIconElement.setName("sprite-goFilter-icon-element");
|
||||||
this.goFilterIconElement.setScale(0.675);
|
this.goFilterIconElement.setScale(0.675);
|
||||||
@ -1497,6 +1519,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
const props = globalScene.gameData.getSpeciesDexAttrProps(randomSpecies, dexAttr);
|
const props = globalScene.gameData.getSpeciesDexAttrProps(randomSpecies, dexAttr);
|
||||||
const abilityIndex = this.abilityCursor;
|
const abilityIndex = this.abilityCursor;
|
||||||
const nature = this.natureCursor as unknown as Nature;
|
const nature = this.natureCursor as unknown as Nature;
|
||||||
|
const teraType = this.teraCursor;
|
||||||
const moveset = this.starterMoveset?.slice(0) as StarterMoveset;
|
const moveset = this.starterMoveset?.slice(0) as StarterMoveset;
|
||||||
const starterCost = globalScene.gameData.getSpeciesStarterValue(randomSpecies.speciesId);
|
const starterCost = globalScene.gameData.getSpeciesStarterValue(randomSpecies.speciesId);
|
||||||
const speciesForm = getPokemonSpeciesForm(randomSpecies.speciesId, props.formIndex);
|
const speciesForm = getPokemonSpeciesForm(randomSpecies.speciesId, props.formIndex);
|
||||||
@ -1505,7 +1528,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
.loadAssets(props.female, props.formIndex, props.shiny, props.variant, true)
|
.loadAssets(props.female, props.formIndex, props.shiny, props.variant, true)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (this.tryUpdateValue(starterCost, true)) {
|
if (this.tryUpdateValue(starterCost, true)) {
|
||||||
this.addToParty(randomSpecies, dexAttr, abilityIndex, nature, moveset, true);
|
this.addToParty(randomSpecies, dexAttr, abilityIndex, nature, moveset, teraType, true);
|
||||||
ui.playSelect();
|
ui.playSelect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1585,7 +1608,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
const cursorObj = this.starterCursorObjs[this.starterSpecies.length];
|
const cursorObj = this.starterCursorObjs[this.starterSpecies.length];
|
||||||
cursorObj.setVisible(true);
|
cursorObj.setVisible(true);
|
||||||
cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y);
|
cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y);
|
||||||
this.addToParty(this.lastSpecies, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset?.slice(0) as StarterMoveset);
|
this.addToParty(this.lastSpecies, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset?.slice(0) as StarterMoveset, this.teraCursor);
|
||||||
ui.playSelect();
|
ui.playSelect();
|
||||||
} else {
|
} else {
|
||||||
ui.playError(); // this should be redundant as there is now a trigger for when a pokemon can't be added to party
|
ui.playError(); // this should be redundant as there is now a trigger for when a pokemon can't be added to party
|
||||||
@ -1961,15 +1984,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
yOffset: 47
|
yOffset: 47
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (!pokemonPrevolutions.hasOwnProperty(this.lastSpecies.speciesId)) {
|
|
||||||
options.push({
|
|
||||||
label: i18next.t("starterSelectUiHandler:useCandies"),
|
|
||||||
handler: () => {
|
|
||||||
ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
options.push({
|
options.push({
|
||||||
label: i18next.t("menuUiHandler:POKEDEX"),
|
label: i18next.t("menuUiHandler:POKEDEX"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@ -1985,6 +1999,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!pokemonPrevolutions.hasOwnProperty(this.lastSpecies.speciesId)) {
|
||||||
|
options.push({
|
||||||
|
label: i18next.t("starterSelectUiHandler:useCandies"),
|
||||||
|
handler: () => {
|
||||||
|
ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
options.push({
|
options.push({
|
||||||
label: i18next.t("menu:cancel"),
|
label: i18next.t("menu:cancel"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@ -2066,7 +2089,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
} while (newFormIndex !== props.formIndex);
|
} while (newFormIndex !== props.formIndex);
|
||||||
starterAttributes.form = newFormIndex; // store the selected form
|
starterAttributes.form = newFormIndex; // store the selected form
|
||||||
this.setSpeciesDetails(this.lastSpecies, { formIndex: newFormIndex });
|
starterAttributes.tera = this.lastSpecies.forms[newFormIndex].type1;
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, { formIndex: newFormIndex, teraType: starterAttributes.tera });
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2125,6 +2149,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Button.CYCLE_TERA:
|
||||||
|
if (this.canCycleTera) {
|
||||||
|
const speciesForm = getPokemonSpeciesForm(this.lastSpecies.speciesId, starterAttributes.form ?? 0);
|
||||||
|
if (speciesForm.type1 === this.teraCursor && !Utils.isNullOrUndefined(speciesForm.type2)) {
|
||||||
|
starterAttributes.tera = speciesForm.type2!;
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, { teraType: speciesForm.type2! });
|
||||||
|
} else {
|
||||||
|
starterAttributes.tera = speciesForm.type1;
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, { teraType: speciesForm.type1 });
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Button.UP:
|
case Button.UP:
|
||||||
if (!this.starterIconsCursorObj.visible) {
|
if (!this.starterIconsCursorObj.visible) {
|
||||||
if (currentRow > 0) {
|
if (currentRow > 0) {
|
||||||
@ -2289,7 +2326,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
return [ isDupe, removeIndex ];
|
return [ isDupe, removeIndex ];
|
||||||
}
|
}
|
||||||
|
|
||||||
addToParty(species: PokemonSpecies, dexAttr: bigint, abilityIndex: number, nature: Nature, moveset: StarterMoveset, randomSelection: boolean = false) {
|
addToParty(species: PokemonSpecies, dexAttr: bigint, abilityIndex: number, nature: Nature, moveset: StarterMoveset, teraType: Type, randomSelection: boolean = false) {
|
||||||
const props = globalScene.gameData.getSpeciesDexAttrProps(species, dexAttr);
|
const props = globalScene.gameData.getSpeciesDexAttrProps(species, dexAttr);
|
||||||
this.starterIcons[this.starterSpecies.length].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant));
|
this.starterIcons[this.starterSpecies.length].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant));
|
||||||
this.starterIcons[this.starterSpecies.length].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant));
|
this.starterIcons[this.starterSpecies.length].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant));
|
||||||
@ -2299,6 +2336,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.starterAttr.push(dexAttr);
|
this.starterAttr.push(dexAttr);
|
||||||
this.starterAbilityIndexes.push(abilityIndex);
|
this.starterAbilityIndexes.push(abilityIndex);
|
||||||
this.starterNatures.push(nature);
|
this.starterNatures.push(nature);
|
||||||
|
this.starterTeras.push(teraType);
|
||||||
this.starterMovesets.push(moveset);
|
this.starterMovesets.push(moveset);
|
||||||
if (this.speciesLoaded.get(species.speciesId) || randomSelection ) {
|
if (this.speciesLoaded.get(species.speciesId) || randomSelection ) {
|
||||||
getPokemonSpeciesForm(species.speciesId, props.formIndex).cry();
|
getPokemonSpeciesForm(species.speciesId, props.formIndex).cry();
|
||||||
@ -2379,6 +2417,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
case SettingKeyboard.Button_Cycle_Nature:
|
case SettingKeyboard.Button_Cycle_Nature:
|
||||||
iconPath = "N.png";
|
iconPath = "N.png";
|
||||||
break;
|
break;
|
||||||
|
case SettingKeyboard.Button_Cycle_Tera:
|
||||||
|
iconPath = "V.png";
|
||||||
|
break;
|
||||||
case SettingKeyboard.Button_Stats:
|
case SettingKeyboard.Button_Stats:
|
||||||
iconPath = "C.png";
|
iconPath = "C.png";
|
||||||
break;
|
break;
|
||||||
@ -2459,6 +2500,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
if (this.canCycleNature) {
|
if (this.canCycleNature) {
|
||||||
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Nature, gamepadType, this.natureIconElement, this.natureLabel);
|
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Nature, gamepadType, this.natureIconElement, this.natureLabel);
|
||||||
}
|
}
|
||||||
|
if (this.canCycleTera) {
|
||||||
|
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Tera, gamepadType, this.teraIconElement, this.teraLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if filter mode is inactivated and gamepadType is not undefined, update the button icons
|
// if filter mode is inactivated and gamepadType is not undefined, update the button icons
|
||||||
@ -2876,6 +2920,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n;
|
this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n;
|
||||||
this.abilityCursor = species ? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0;
|
this.abilityCursor = species ? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0;
|
||||||
this.natureCursor = species ? globalScene.gameData.getSpeciesDefaultNature(species) : 0;
|
this.natureCursor = species ? globalScene.gameData.getSpeciesDefaultNature(species) : 0;
|
||||||
|
this.teraCursor = species ? species.type1 : Type.UNKNOWN;
|
||||||
|
|
||||||
if (!species && globalScene.ui.getTooltip().visible) {
|
if (!species && globalScene.ui.getTooltip().visible) {
|
||||||
globalScene.ui.hideTooltip();
|
globalScene.ui.hideTooltip();
|
||||||
@ -2894,6 +2939,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
// load default ability from stater save data, if set
|
// load default ability from stater save data, if set
|
||||||
this.abilityCursor = starterAttributes.ability;
|
this.abilityCursor = starterAttributes.ability;
|
||||||
}
|
}
|
||||||
|
if (starterAttributes?.tera) {
|
||||||
|
// load default tera from starter save data, if set
|
||||||
|
this.teraCursor = starterAttributes.tera;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.statsMode) {
|
if (this.statsMode) {
|
||||||
if (this.speciesStarterDexEntry?.caughtAttr) {
|
if (this.speciesStarterDexEntry?.caughtAttr) {
|
||||||
@ -3035,7 +3084,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
female: props.female,
|
female: props.female,
|
||||||
variant: props.variant,
|
variant: props.variant,
|
||||||
abilityIndex: this.starterAbilityIndexes[starterIndex],
|
abilityIndex: this.starterAbilityIndexes[starterIndex],
|
||||||
natureIndex: this.starterNatures[starterIndex]
|
natureIndex: this.starterNatures[starterIndex],
|
||||||
|
teraType: this.starterTeras[starterIndex]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
|
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
|
||||||
@ -3083,6 +3133,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonCaughtHatchedContainer.setVisible(false);
|
this.pokemonCaughtHatchedContainer.setVisible(false);
|
||||||
this.pokemonCandyContainer.setVisible(false);
|
this.pokemonCandyContainer.setVisible(false);
|
||||||
this.pokemonFormText.setVisible(false);
|
this.pokemonFormText.setVisible(false);
|
||||||
|
this.teraIcon.setVisible(false);
|
||||||
|
|
||||||
const defaultDexAttr = globalScene.gameData.getSpeciesDefaultDexAttr(species, true, true);
|
const defaultDexAttr = globalScene.gameData.getSpeciesDefaultDexAttr(species, true, true);
|
||||||
const defaultAbilityIndex = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
const defaultAbilityIndex = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
||||||
@ -3117,6 +3168,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonCaughtHatchedContainer.setVisible(false);
|
this.pokemonCaughtHatchedContainer.setVisible(false);
|
||||||
this.pokemonCandyContainer.setVisible(false);
|
this.pokemonCandyContainer.setVisible(false);
|
||||||
this.pokemonFormText.setVisible(false);
|
this.pokemonFormText.setVisible(false);
|
||||||
|
this.teraIcon.setVisible(false);
|
||||||
|
|
||||||
this.setSpeciesDetails(species!, { // TODO: is this bang correct?
|
this.setSpeciesDetails(species!, { // TODO: is this bang correct?
|
||||||
shiny: false,
|
shiny: false,
|
||||||
@ -3131,7 +3183,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void {
|
setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void {
|
||||||
let { shiny, formIndex, female, variant, abilityIndex, natureIndex } = options;
|
let { shiny, formIndex, female, variant, abilityIndex, natureIndex, teraType } = options;
|
||||||
const forSeen: boolean = options.forSeen ?? false;
|
const forSeen: boolean = options.forSeen ?? false;
|
||||||
const oldProps = species ? globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
|
const oldProps = species ? globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
|
||||||
const oldAbilityIndex = this.abilityCursor > -1 ? this.abilityCursor : globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
const oldAbilityIndex = this.abilityCursor > -1 ? this.abilityCursor : globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
||||||
@ -3139,6 +3191,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.dexAttrCursor = 0n;
|
this.dexAttrCursor = 0n;
|
||||||
this.abilityCursor = -1;
|
this.abilityCursor = -1;
|
||||||
this.natureCursor = -1;
|
this.natureCursor = -1;
|
||||||
|
this.teraCursor = Type.UNKNOWN;
|
||||||
// We will only update the sprite if there is a change to form, shiny/variant
|
// We will only update the sprite if there is a change to form, shiny/variant
|
||||||
// or gender for species with gender sprite differences
|
// or gender for species with gender sprite differences
|
||||||
const shouldUpdateSprite = (species?.genderDiffs && !isNullOrUndefined(female))
|
const shouldUpdateSprite = (species?.genderDiffs && !isNullOrUndefined(female))
|
||||||
@ -3168,6 +3221,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.dexAttrCursor |= globalScene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps!.formIndex)); // TODO: is this bang correct?
|
this.dexAttrCursor |= globalScene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps!.formIndex)); // TODO: is this bang correct?
|
||||||
this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex);
|
this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex);
|
||||||
this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex);
|
this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex);
|
||||||
|
this.teraCursor = !Utils.isNullOrUndefined(teraType) ? teraType : (teraType = species.type1);
|
||||||
const [ isInParty, partyIndex ]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image
|
const [ isInParty, partyIndex ]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image
|
||||||
if (isInParty) {
|
if (isInParty) {
|
||||||
this.updatePartyIcon(species, partyIndex);
|
this.updatePartyIcon(species, partyIndex);
|
||||||
@ -3179,6 +3233,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonPassiveText.setVisible(false);
|
this.pokemonPassiveText.setVisible(false);
|
||||||
this.pokemonPassiveDisabledIcon.setVisible(false);
|
this.pokemonPassiveDisabledIcon.setVisible(false);
|
||||||
this.pokemonPassiveLockedIcon.setVisible(false);
|
this.pokemonPassiveLockedIcon.setVisible(false);
|
||||||
|
this.teraIcon.setVisible(false);
|
||||||
|
|
||||||
if (this.assetLoadCancelled) {
|
if (this.assetLoadCancelled) {
|
||||||
this.assetLoadCancelled.value = true;
|
this.assetLoadCancelled.value = true;
|
||||||
@ -3230,6 +3285,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.starterAttr[starterIndex] = this.dexAttrCursor;
|
this.starterAttr[starterIndex] = this.dexAttrCursor;
|
||||||
this.starterAbilityIndexes[starterIndex] = this.abilityCursor;
|
this.starterAbilityIndexes[starterIndex] = this.abilityCursor;
|
||||||
this.starterNatures[starterIndex] = this.natureCursor;
|
this.starterNatures[starterIndex] = this.natureCursor;
|
||||||
|
this.starterTeras[starterIndex] = this.teraCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetLoadCancelled = new BooleanHolder(false);
|
const assetLoadCancelled = new BooleanHolder(false);
|
||||||
@ -3288,7 +3344,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey))
|
this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey))
|
||||||
.map((_, f) => dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f)).filter(f => f).length > 1;
|
.map((_, f) => dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f)).filter(f => f).length > 1;
|
||||||
this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
|
this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
|
||||||
|
this.canCycleTera = globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && !Utils.isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dexEntry.caughtAttr && species.malePercent !== null) {
|
if (dexEntry.caughtAttr && species.malePercent !== null) {
|
||||||
@ -3412,10 +3468,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonFormText.setText(formText);
|
this.pokemonFormText.setText(formText);
|
||||||
|
|
||||||
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
|
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
|
||||||
|
|
||||||
|
this.teraIcon.setFrame(Type[this.teraCursor].toLowerCase());
|
||||||
|
this.teraIcon.setVisible(!this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id));
|
||||||
} else {
|
} else {
|
||||||
this.pokemonAbilityText.setText("");
|
this.pokemonAbilityText.setText("");
|
||||||
this.pokemonPassiveText.setText("");
|
this.pokemonPassiveText.setText("");
|
||||||
this.pokemonNatureText.setText("");
|
this.pokemonNatureText.setText("");
|
||||||
|
this.teraIcon.setVisible(false);
|
||||||
this.setTypeIcons(null, null);
|
this.setTypeIcons(null, null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3426,6 +3486,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonAbilityText.setText("");
|
this.pokemonAbilityText.setText("");
|
||||||
this.pokemonPassiveText.setText("");
|
this.pokemonPassiveText.setText("");
|
||||||
this.pokemonNatureText.setText("");
|
this.pokemonNatureText.setText("");
|
||||||
|
this.teraIcon.setVisible(false);
|
||||||
this.setTypeIcons(null, null);
|
this.setTypeIcons(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3479,6 +3540,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.starterAttr.splice(index, 1);
|
this.starterAttr.splice(index, 1);
|
||||||
this.starterAbilityIndexes.splice(index, 1);
|
this.starterAbilityIndexes.splice(index, 1);
|
||||||
this.starterNatures.splice(index, 1);
|
this.starterNatures.splice(index, 1);
|
||||||
|
this.starterTeras.splice(index, 1);
|
||||||
this.starterMovesets.splice(index, 1);
|
this.starterMovesets.splice(index, 1);
|
||||||
|
|
||||||
for (let s = 0; s < this.starterSpecies.length; s++) {
|
for (let s = 0; s < this.starterSpecies.length; s++) {
|
||||||
@ -3690,6 +3752,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
abilityIndex: thisObj.starterAbilityIndexes[i],
|
abilityIndex: thisObj.starterAbilityIndexes[i],
|
||||||
passive: !(globalScene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)),
|
passive: !(globalScene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)),
|
||||||
nature: thisObj.starterNatures[i] as Nature,
|
nature: thisObj.starterNatures[i] as Nature,
|
||||||
|
teraType: thisObj.starterTeras[i] as Type,
|
||||||
moveset: thisObj.starterMovesets[i],
|
moveset: thisObj.starterMovesets[i],
|
||||||
pokerus: thisObj.pokerusSpecies.includes(starterSpecies),
|
pokerus: thisObj.pokerusSpecies.includes(starterSpecies),
|
||||||
nickname: thisObj.starterPreferences[starterSpecies.speciesId]?.nickname,
|
nickname: thisObj.starterPreferences[starterSpecies.speciesId]?.nickname,
|
||||||
@ -3816,6 +3879,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.abilityLabel.setVisible(false);
|
this.abilityLabel.setVisible(false);
|
||||||
this.natureIconElement.setVisible(false);
|
this.natureIconElement.setVisible(false);
|
||||||
this.natureLabel.setVisible(false);
|
this.natureLabel.setVisible(false);
|
||||||
|
this.teraIconElement.setVisible(false);
|
||||||
|
this.teraLabel.setVisible(false);
|
||||||
this.goFilterIconElement.setVisible(false);
|
this.goFilterIconElement.setVisible(false);
|
||||||
this.goFilterLabel.setVisible(false);
|
this.goFilterLabel.setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||||||
this.summaryContainer.add(this.championRibbon);
|
this.summaryContainer.add(this.championRibbon);
|
||||||
this.championRibbon.setVisible(false);
|
this.championRibbon.setVisible(false);
|
||||||
|
|
||||||
this.levelText = addTextObject(36, -17, "", TextStyle.SUMMARY_ALT);
|
this.levelText = addTextObject(24, -17, "", TextStyle.SUMMARY_ALT);
|
||||||
this.levelText.setOrigin(0, 1);
|
this.levelText.setOrigin(0, 1);
|
||||||
this.summaryContainer.add(this.levelText);
|
this.summaryContainer.add(this.levelText);
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball));
|
this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball));
|
||||||
this.levelText.setText(this.pokemon.level.toString());
|
this.levelText.setText(`${i18next.t("pokemonSummary:lv")}${this.pokemon.level.toString()}`);
|
||||||
this.genderText.setText(getGenderSymbol(this.pokemon.getGender(true)));
|
this.genderText.setText(getGenderSymbol(this.pokemon.getGender(true)));
|
||||||
this.genderText.setColor(getGenderColor(this.pokemon.getGender(true)));
|
this.genderText.setColor(getGenderColor(this.pokemon.getGender(true)));
|
||||||
this.genderText.setShadowColor(getGenderColor(this.pokemon.getGender(true), true));
|
this.genderText.setShadowColor(getGenderColor(this.pokemon.getGender(true), true));
|
||||||
@ -756,7 +756,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||||||
trainerText.setOrigin(0, 0);
|
trainerText.setOrigin(0, 0);
|
||||||
profileContainer.add(trainerText);
|
profileContainer.add(trainerText);
|
||||||
|
|
||||||
const trainerIdText = addTextObject(174, 12, globalScene.gameData.trainerId.toString(), TextStyle.SUMMARY_ALT);
|
const trainerIdText = addTextObject(141, 12, `${i18next.t("pokemonSummary:idNo")}${globalScene.gameData.trainerId.toString()}`, TextStyle.SUMMARY_ALT);
|
||||||
trainerIdText.setOrigin(0, 0);
|
trainerIdText.setOrigin(0, 0);
|
||||||
profileContainer.add(trainerIdText);
|
profileContainer.add(trainerIdText);
|
||||||
|
|
||||||
|