Merge branch 'beta' into deep-sea-items

This commit is contained in:
Madmadness65 2025-01-12 17:56:08 -06:00 committed by GitHub
commit 3e479f337d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
393 changed files with 9523 additions and 9454 deletions

View File

@ -46,6 +46,7 @@ export default [
"computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets "computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets
"space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators "space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines
"@typescript-eslint/consistent-type-imports": "error", // Enforces type-only imports wherever possible
} }
} }
] ]

View File

@ -1,3 +1 @@
import BattleScene from "#app/battle-scene"; export type ConditionFn = (args?: any[]) => boolean;
export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean;

View File

@ -1,4 +1,4 @@
import { TOptions } from "i18next"; import type { TOptions } from "i18next";
// Module declared to make referencing keys in the localization files type-safe. // Module declared to make referencing keys in the localization files type-safe.
declare module "i18next" { declare module "i18next" {

View File

@ -1,13 +1,18 @@
import Phaser from "phaser"; import Phaser from "phaser";
import UI from "#app/ui/ui"; import UI from "#app/ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils"; import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { Constructor } from "#app/utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, ModifierBar, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier } from "./modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import { Phase } from "#app/phase"; import type { Phase } from "#app/phase";
import { initGameSpeed } from "#app/system/game-speed"; import { initGameSpeed } from "#app/system/game-speed";
import { Arena, ArenaBase } from "#app/field/arena"; import { Arena, ArenaBase } from "#app/field/arena";
import { GameData } from "#app/system/game-data"; import { GameData } from "#app/system/game-data";
@ -17,26 +22,32 @@ import { MusicPreference } from "#app/system/settings/settings";
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import AbilityBar from "#app/ui/ability-bar"; import AbilityBar from "#app/ui/ability-bar";
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability"; import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability";
import Battle, { BattleType, FixedBattleConfig } from "#app/battle"; import type { FixedBattleConfig } from "#app/battle";
import { GameMode, GameModes, getGameMode } from "#app/game-mode"; import Battle, { BattleType } from "#app/battle";
import type { GameMode } from "#app/game-mode";
import { GameModes, getGameMode } from "#app/game-mode";
import FieldSpritePipeline from "#app/pipelines/field-sprite"; import FieldSpritePipeline from "#app/pipelines/field-sprite";
import SpritePipeline from "#app/pipelines/sprite"; import SpritePipeline from "#app/pipelines/sprite";
import PartyExpBar from "#app/ui/party-exp-bar"; import PartyExpBar from "#app/ui/party-exp-bar";
import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config"; import type { TrainerSlot } from "#app/data/trainer-config";
import { trainerConfigs } from "#app/data/trainer-config";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import TrainerData from "#app/system/trainer-data"; import type TrainerData from "#app/system/trainer-data";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokeballTray from "#app/ui/pokeball-tray"; import PokeballTray from "#app/ui/pokeball-tray";
import InvertPostFX from "#app/pipelines/invert"; import InvertPostFX from "#app/pipelines/invert";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv"; import type { Achv } from "#app/system/achv";
import { Voucher, vouchers } from "#app/system/voucher"; import { achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
import type { Voucher } from "#app/system/voucher";
import { vouchers } from "#app/system/voucher";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import { addUiThemeOverrides } from "#app/ui/ui-theme"; import { addUiThemeOverrides } from "#app/ui/ui-theme";
import PokemonData from "#app/system/pokemon-data"; import type PokemonData from "#app/system/pokemon-data";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms"; import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger } from "#app/data/pokemon-forms";
import { FormChangePhase } from "#app/phases/form-change-phase"; import { FormChangePhase } from "#app/phases/form-change-phase";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
@ -47,8 +58,9 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes"; import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "#app/scene-base"; import { SceneBase } from "#app/scene-base";
import CandyBar from "#app/ui/candy-bar"; import CandyBar from "#app/ui/candy-bar";
import { Variant, variantColorCache, variantData, VariantSet } from "#app/data/variant"; import type { Variant, VariantSet } from "#app/data/variant";
import { Localizable } from "#app/interfaces/locales"; import { variantColorCache, variantData } from "#app/data/variant";
import type { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { InputsController } from "#app/inputs-controller"; import { InputsController } from "#app/inputs-controller";
import { UiInputs } from "#app/ui-inputs"; import { UiInputs } from "#app/ui-inputs";
@ -58,14 +70,14 @@ import { EaseType } from "#enums/ease-type";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { BattleStyle } from "#enums/battle-style"; import { BattleStyle } from "#enums/battle-style";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { ExpNotification } from "#enums/exp-notification"; import type { ExpNotification } from "#enums/exp-notification";
import { MoneyFormat } from "#enums/money-format"; import { MoneyFormat } from "#enums/money-format";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { UiTheme } from "#enums/ui-theme"; import { UiTheme } from "#enums/ui-theme";
import { TimedEventManager } from "#app/timed-event-manager"; import { TimedEventManager } from "#app/timed-event-manager";
import { PokemonAnimType } from "#enums/pokemon-anim-type"; import type { PokemonAnimType } from "#enums/pokemon-anim-type";
import i18next from "i18next"; import i18next from "i18next";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { battleSpecDialogue } from "#app/data/dialogue"; import { battleSpecDialogue } from "#app/data/dialogue";
@ -92,7 +104,7 @@ import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase"; import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
@ -100,6 +112,7 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { globalScene, initGlobalScene } from "#app/global-scene";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -330,6 +343,7 @@ export default class BattleScene extends SceneBase {
this.nextCommandPhaseQueue = []; this.nextCommandPhaseQueue = [];
this.eventManager = new TimedEventManager(); this.eventManager = new TimedEventManager();
this.updateGameInfo(); this.updateGameInfo();
initGlobalScene(this);
} }
loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) { loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) {
@ -375,14 +389,13 @@ export default class BattleScene extends SceneBase {
async preload() { async preload() {
if (DEBUG_RNG) { if (DEBUG_RNG) {
const scene = this;
const originalRealInRange = Phaser.Math.RND.realInRange; const originalRealInRange = Phaser.Math.RND.realInRange;
Phaser.Math.RND.realInRange = function (min: number, max: number): number { Phaser.Math.RND.realInRange = function (min: number, max: number): number {
const ret = originalRealInRange.apply(this, [ min, max ]); const ret = originalRealInRange.apply(this, [ min, max ]);
const args = [ "RNG", ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ]; const args = [ "RNG", ++globalScene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`); args.push(`seed: ${globalScene.rngSeedOverride || globalScene.waveSeed || globalScene.seed}`);
if (scene.rngOffset) { if (globalScene.rngOffset) {
args.push(`offset: ${scene.rngOffset}`); args.push(`offset: ${globalScene.rngOffset}`);
} }
console.log(...args); console.log(...args);
return ret; return ret;
@ -395,14 +408,14 @@ export default class BattleScene extends SceneBase {
} }
create() { create() {
this.scene.remove(LoadingScene.KEY); globalScene.scene.remove(LoadingScene.KEY);
initGameSpeed.apply(this); initGameSpeed.apply(this);
this.inputController = new InputsController(this); this.inputController = new InputsController();
this.uiInputs = new UiInputs(this, this.inputController); this.uiInputs = new UiInputs(this.inputController);
this.gameData = new GameData(this); this.gameData = new GameData();
addUiThemeOverrides(this); addUiThemeOverrides();
this.load.setBaseURL(); this.load.setBaseURL();
@ -489,76 +502,76 @@ export default class BattleScene extends SceneBase {
this.modifiers = []; this.modifiers = [];
this.enemyModifiers = []; this.enemyModifiers = [];
this.modifierBar = new ModifierBar(this); this.modifierBar = new ModifierBar();
this.modifierBar.setName("modifier-bar"); this.modifierBar.setName("modifier-bar");
this.add.existing(this.modifierBar); this.add.existing(this.modifierBar);
uiContainer.add(this.modifierBar); uiContainer.add(this.modifierBar);
this.enemyModifierBar = new ModifierBar(this, true); this.enemyModifierBar = new ModifierBar(true);
this.enemyModifierBar.setName("enemy-modifier-bar"); this.enemyModifierBar.setName("enemy-modifier-bar");
this.add.existing(this.enemyModifierBar); this.add.existing(this.enemyModifierBar);
uiContainer.add(this.enemyModifierBar); uiContainer.add(this.enemyModifierBar);
this.charSprite = new CharSprite(this); this.charSprite = new CharSprite();
this.charSprite.setName("sprite-char"); this.charSprite.setName("sprite-char");
this.charSprite.setup(); this.charSprite.setup();
this.fieldUI.add(this.charSprite); this.fieldUI.add(this.charSprite);
this.pbTray = new PokeballTray(this, true); this.pbTray = new PokeballTray(true);
this.pbTray.setName("pb-tray"); this.pbTray.setName("pb-tray");
this.pbTray.setup(); this.pbTray.setup();
this.pbTrayEnemy = new PokeballTray(this, false); this.pbTrayEnemy = new PokeballTray(false);
this.pbTrayEnemy.setName("enemy-pb-tray"); this.pbTrayEnemy.setName("enemy-pb-tray");
this.pbTrayEnemy.setup(); this.pbTrayEnemy.setup();
this.fieldUI.add(this.pbTray); this.fieldUI.add(this.pbTray);
this.fieldUI.add(this.pbTrayEnemy); this.fieldUI.add(this.pbTrayEnemy);
this.abilityBar = new AbilityBar(this); this.abilityBar = new AbilityBar();
this.abilityBar.setName("ability-bar"); this.abilityBar.setName("ability-bar");
this.abilityBar.setup(); this.abilityBar.setup();
this.fieldUI.add(this.abilityBar); this.fieldUI.add(this.abilityBar);
this.partyExpBar = new PartyExpBar(this); this.partyExpBar = new PartyExpBar();
this.partyExpBar.setName("party-exp-bar"); this.partyExpBar.setName("party-exp-bar");
this.partyExpBar.setup(); this.partyExpBar.setup();
this.fieldUI.add(this.partyExpBar); this.fieldUI.add(this.partyExpBar);
this.candyBar = new CandyBar(this); this.candyBar = new CandyBar();
this.candyBar.setName("candy-bar"); this.candyBar.setName("candy-bar");
this.candyBar.setup(); this.candyBar.setup();
this.fieldUI.add(this.candyBar); this.fieldUI.add(this.candyBar);
this.biomeWaveText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO); this.biomeWaveText = addTextObject((this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
this.biomeWaveText.setName("text-biome-wave"); this.biomeWaveText.setName("text-biome-wave");
this.biomeWaveText.setOrigin(1, 0.5); this.biomeWaveText.setOrigin(1, 0.5);
this.fieldUI.add(this.biomeWaveText); this.fieldUI.add(this.biomeWaveText);
this.moneyText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY); this.moneyText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY);
this.moneyText.setName("text-money"); this.moneyText.setName("text-money");
this.moneyText.setOrigin(1, 0.5); this.moneyText.setOrigin(1, 0.5);
this.fieldUI.add(this.moneyText); this.fieldUI.add(this.moneyText);
this.scoreText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.scoreText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.scoreText.setName("text-score"); this.scoreText.setName("text-score");
this.scoreText.setOrigin(1, 0.5); this.scoreText.setOrigin(1, 0.5);
this.fieldUI.add(this.scoreText); this.fieldUI.add(this.scoreText);
this.luckText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.luckText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.luckText.setName("text-luck"); this.luckText.setName("text-luck");
this.luckText.setOrigin(1, 0.5); this.luckText.setOrigin(1, 0.5);
this.luckText.setVisible(false); this.luckText.setVisible(false);
this.fieldUI.add(this.luckText); this.fieldUI.add(this.luckText);
this.luckLabelText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, i18next.t("common:luckIndicator"), TextStyle.PARTY, { fontSize: "54px" }); this.luckLabelText = addTextObject((this.game.canvas.width / 6) - 2, 0, i18next.t("common:luckIndicator"), TextStyle.PARTY, { fontSize: "54px" });
this.luckLabelText.setName("text-luck-label"); this.luckLabelText.setName("text-luck-label");
this.luckLabelText.setOrigin(1, 0.5); this.luckLabelText.setOrigin(1, 0.5);
this.luckLabelText.setVisible(false); this.luckLabelText.setVisible(false);
this.fieldUI.add(this.luckLabelText); this.fieldUI.add(this.luckLabelText);
this.arenaFlyout = new ArenaFlyout(this); this.arenaFlyout = new ArenaFlyout();
this.fieldUI.add(this.arenaFlyout); this.fieldUI.add(this.arenaFlyout);
this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay); this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay);
@ -567,9 +580,9 @@ export default class BattleScene extends SceneBase {
this.damageNumberHandler = new DamageNumberHandler(); this.damageNumberHandler = new DamageNumberHandler();
this.spriteSparkleHandler = new PokemonSpriteSparkleHandler(); this.spriteSparkleHandler = new PokemonSpriteSparkleHandler();
this.spriteSparkleHandler.setup(this); this.spriteSparkleHandler.setup();
this.pokemonInfoContainer = new PokemonInfoContainer(this, (this.game.canvas.width / 6) + 52, -(this.game.canvas.height / 6) + 66); this.pokemonInfoContainer = new PokemonInfoContainer((this.game.canvas.width / 6) + 52, -(this.game.canvas.height / 6) + 66);
this.pokemonInfoContainer.setup(); this.pokemonInfoContainer.setup();
this.fieldUI.add(this.pokemonInfoContainer); this.fieldUI.add(this.pokemonInfoContainer);
@ -578,13 +591,13 @@ export default class BattleScene extends SceneBase {
const loadPokemonAssets = []; const loadPokemonAssets = [];
this.arenaPlayer = new ArenaBase(this, true); this.arenaPlayer = new ArenaBase(true);
this.arenaPlayer.setName("arena-player"); this.arenaPlayer.setName("arena-player");
this.arenaPlayerTransition = new ArenaBase(this, true); this.arenaPlayerTransition = new ArenaBase(true);
this.arenaPlayerTransition.setName("arena-player-transition"); this.arenaPlayerTransition.setName("arena-player-transition");
this.arenaEnemy = new ArenaBase(this, false); this.arenaEnemy = new ArenaBase(false);
this.arenaEnemy.setName("arena-enemy"); this.arenaEnemy.setName("arena-enemy");
this.arenaNextEnemy = new ArenaBase(this, false); this.arenaNextEnemy = new ArenaBase(false);
this.arenaNextEnemy.setName("arena-next-enemy"); this.arenaNextEnemy.setName("arena-next-enemy");
this.arenaBgTransition.setVisible(false); this.arenaBgTransition.setVisible(false);
@ -625,7 +638,7 @@ export default class BattleScene extends SceneBase {
this.reset(false, false, true); this.reset(false, false, true);
const ui = new UI(this); const ui = new UI();
this.uiContainer.add(ui); this.uiContainer.add(ui);
this.ui = ui; this.ui = ui;
@ -636,12 +649,12 @@ export default class BattleScene extends SceneBase {
Promise.all([ Promise.all([
Promise.all(loadPokemonAssets), Promise.all(loadPokemonAssets),
initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)), initCommonAnims().then(() => loadCommonAnimAssets(true)),
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)),
this.initStarterColors() this.initStarterColors()
]).then(() => { ]).then(() => {
this.pushPhase(new LoginPhase(this)); this.pushPhase(new LoginPhase());
this.pushPhase(new TitlePhase(this)); this.pushPhase(new TitlePhase());
this.shiftPhase(); this.shiftPhase();
}); });
@ -911,7 +924,7 @@ export default class BattleScene extends SceneBase {
} }
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); const pokemon = new PlayerPokemon(species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource);
if (postProcess) { if (postProcess) {
postProcess(pokemon); postProcess(pokemon);
} }
@ -929,7 +942,7 @@ export default class BattleScene extends SceneBase {
boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1; boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1;
} }
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, shinyLock, dataSource); const pokemon = new EnemyPokemon(species, level, trainerSlot, boss, shinyLock, dataSource);
if (Overrides.OPP_FUSION_OVERRIDE) { if (Overrides.OPP_FUSION_OVERRIDE) {
pokemon.generateFusionSpecies(); pokemon.generateFusionSpecies();
} }
@ -1064,7 +1077,7 @@ export default class BattleScene extends SceneBase {
/** /**
* Generates a random number using the current battle's seed * Generates a random number using the current battle's seed
* *
* This calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts` * This calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts`
* which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` * which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`
* *
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min} * @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
@ -1072,12 +1085,12 @@ export default class BattleScene extends SceneBase {
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/ */
randBattleSeedInt(range: integer, min: integer = 0): integer { randBattleSeedInt(range: integer, min: integer = 0): integer {
return this.currentBattle?.randSeedInt(this, range, min); return this.currentBattle?.randSeedInt(range, min);
} }
reset(clearScene: boolean = false, clearData: boolean = false, reloadI18n: boolean = false): void { reset(clearScene: boolean = false, clearData: boolean = false, reloadI18n: boolean = false): void {
if (clearData) { if (clearData) {
this.gameData = new GameData(this); this.gameData = new GameData();
} }
this.gameMode = getGameMode(GameModes.CLASSIC); this.gameMode = getGameMode(GameModes.CLASSIC);
@ -1210,7 +1223,7 @@ export default class BattleScene extends SceneBase {
battleConfig = this.gameMode.getFixedBattle(newWaveIndex); battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
newDouble = battleConfig.double; newDouble = battleConfig.double;
newBattleType = battleConfig.battleType; newBattleType = battleConfig.battleType;
this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8);
if (newTrainer) { if (newTrainer) {
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1236,7 +1249,7 @@ export default class BattleScene extends SceneBase {
} }
} }
const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant); newTrainer = trainerData !== undefined ? trainerData.toTrainer() : new Trainer(trainerType, variant);
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1314,7 +1327,7 @@ export default class BattleScene extends SceneBase {
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble); this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
}, newWaveIndex << 3, this.waveSeed); }, newWaveIndex << 3, this.waveSeed);
this.currentBattle.incrementTurn(this); this.currentBattle.incrementTurn();
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) { if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
// Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome // Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome
@ -1342,7 +1355,7 @@ export default class BattleScene extends SceneBase {
playerField.forEach((pokemon, p) => { playerField.forEach((pokemon, p) => {
if (pokemon.isOnField()) { if (pokemon.isOnField()) {
this.pushPhase(new ReturnPhase(this, p)); this.pushPhase(new ReturnPhase(p));
} }
}); });
@ -1352,7 +1365,7 @@ export default class BattleScene extends SceneBase {
} }
if (!this.trainer.visible) { if (!this.trainer.visible) {
this.pushPhase(new ShowTrainerPhase(this)); this.pushPhase(new ShowTrainerPhase());
} }
} }
@ -1361,14 +1374,14 @@ export default class BattleScene extends SceneBase {
} }
if (!this.gameMode.hasRandomBiomes && !isNewBiome) { if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
this.pushPhase(new NextEncounterPhase(this)); this.pushPhase(new NextEncounterPhase());
} else { } else {
this.pushPhase(new SelectBiomePhase(this)); this.pushPhase(new SelectBiomePhase());
this.pushPhase(new NewBiomeEncounterPhase(this)); this.pushPhase(new NewBiomeEncounterPhase());
const newMaxExpLevel = this.getMaxExpLevel(); const newMaxExpLevel = this.getMaxExpLevel();
if (newMaxExpLevel > maxExpLevel) { if (newMaxExpLevel > maxExpLevel) {
this.pushPhase(new LevelCapPhase(this)); this.pushPhase(new LevelCapPhase());
} }
} }
} }
@ -1377,7 +1390,7 @@ export default class BattleScene extends SceneBase {
} }
newArena(biome: Biome): Arena { newArena(biome: Biome): Arena {
this.arena = new Arena(this, biome, Biome[biome].toLowerCase()); this.arena = new Arena(biome, Biome[biome].toLowerCase());
this.eventTarget.dispatchEvent(new NewArenaEvent()); this.eventTarget.dispatchEvent(new NewArenaEvent());
this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() }; this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() };
@ -1793,7 +1806,7 @@ export default class BattleScene extends SceneBase {
} }
updateUIPositions(): void { updateUIPositions(): void {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible(this)).length; const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length;
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY( this.biomeWaveText.setY(
-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2) -(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2)
@ -1880,7 +1893,7 @@ export default class BattleScene extends SceneBase {
playBgm(bgmName?: string, fadeOut?: boolean): void { playBgm(bgmName?: string, fadeOut?: boolean): void {
if (bgmName === undefined) { if (bgmName === undefined) {
bgmName = this.currentBattle?.getBgmOverride(this) || this.arena?.bgm; bgmName = this.currentBattle?.getBgmOverride() || this.arena?.bgm;
} }
if (this.bgm && bgmName === this.bgm.key) { if (this.bgm && bgmName === this.bgm.key) {
if (!this.bgm.isPlaying) { if (!this.bgm.isPlaying) {
@ -2499,7 +2512,7 @@ export default class BattleScene extends SceneBase {
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue * @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue
*/ */
queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) { queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) {
const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay); const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
if (!defer) { if (!defer) {
// adds to the end of PhaseQueuePrepend // adds to the end of PhaseQueuePrepend
this.unshiftPhase(phase); this.unshiftPhase(phase);
@ -2517,7 +2530,7 @@ export default class BattleScene extends SceneBase {
this.phaseQueue.push(...this.nextCommandPhaseQueue); this.phaseQueue.push(...this.nextCommandPhaseQueue);
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length); this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
} }
this.phaseQueue.push(new TurnInitPhase(this)); this.phaseQueue.push(new TurnInitPhase());
} }
addMoney(amount: integer): void { addMoney(amount: integer): void {
@ -2548,7 +2561,7 @@ export default class BattleScene extends SceneBase {
if (modifier instanceof TerastallizeModifier) { if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId))); modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId)));
} }
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) { if ((modifier as PersistentModifier).add(this.modifiers, !!virtual)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
@ -2629,7 +2642,7 @@ export default class BattleScene extends SceneBase {
if (modifier instanceof TerastallizeModifier) { if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false))); modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false)));
} }
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) { if ((modifier as PersistentModifier).add(this.enemyModifiers, false)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
@ -2664,7 +2677,7 @@ export default class BattleScene extends SceneBase {
*/ */
tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean, transferQuantity: number = 1, instant?: boolean, ignoreUpdate?: boolean, itemLost: boolean = true): Promise<boolean> { tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean, transferQuantity: number = 1, instant?: boolean, ignoreUpdate?: boolean, itemLost: boolean = true): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null; const source = itemModifier.pokemonId ? itemModifier.getPokemon() : null;
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => { Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => {
if (cancelled.value) { if (cancelled.value) {
@ -2672,11 +2685,11 @@ export default class BattleScene extends SceneBase {
} }
const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier; const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier;
newItemModifier.pokemonId = target.id; newItemModifier.pokemonId = target.id;
const matchingModifier = target.scene.findModifier(m => m instanceof PokemonHeldItemModifier const matchingModifier = this.findModifier(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier; && (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier;
let removeOld = true; let removeOld = true;
if (matchingModifier) { if (matchingModifier) {
const maxStackCount = matchingModifier.getMaxStackCount(target.scene); const maxStackCount = matchingModifier.getMaxStackCount();
if (matchingModifier.stackCount >= maxStackCount) { if (matchingModifier.stackCount >= maxStackCount) {
return resolve(false); return resolve(false);
} }
@ -2793,7 +2806,7 @@ export default class BattleScene extends SceneBase {
count = Math.max(count, Math.floor(chances / 2)); count = Math.max(count, Math.floor(chances / 2));
} }
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance) getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false));
} }
return true; return true;
}); });
@ -2817,7 +2830,7 @@ export default class BattleScene extends SceneBase {
* @param pokemon - If specified, only removes held items from that {@linkcode Pokemon} * @param pokemon - If specified, only removes held items from that {@linkcode Pokemon}
*/ */
clearEnemyHeldItemModifiers(pokemon?: Pokemon): void { clearEnemyHeldItemModifiers(pokemon?: Pokemon): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon)); const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon() === pokemon));
for (const m of modifiersToRemove) { for (const m of modifiersToRemove) {
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1); this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
} }
@ -2866,9 +2879,7 @@ export default class BattleScene extends SceneBase {
updatePartyForModifiers(party: Pokemon[], instant?: boolean): Promise<void> { updatePartyForModifiers(party: Pokemon[], instant?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
Promise.allSettled(party.map(p => { Promise.allSettled(party.map(p => {
if (p.scene) { p.calculateStats();
p.calculateStats();
}
return p.updateInfo(instant); return p.updateInfo(instant);
})).then(() => resolve()); })).then(() => resolve());
}); });
@ -2931,15 +2942,14 @@ export default class BattleScene extends SceneBase {
/** /**
* Apply all modifiers that match `modifierType` in a random order * Apply all modifiers that match `modifierType` in a random order
* @param scene {@linkcode BattleScene} used to randomize the order of modifiers
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier} * @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true` * @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply` * @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied. * @returns the list of all modifiers that matched `modifierType` and were applied.
*/ */
applyShuffledModifiers<T extends PersistentModifier>(scene: BattleScene, modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] { applyShuffledModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args)); let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const shuffleModifiers = mods => { const shuffleModifiers = mods => {
if (mods.length < 1) { if (mods.length < 1) {
return mods; return mods;
@ -2948,7 +2958,7 @@ export default class BattleScene extends SceneBase {
return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ]; return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ];
}; };
modifiers = shuffleModifiers(modifiers); modifiers = shuffleModifiers(modifiers);
}, scene.currentBattle.turn << 4, scene.waveSeed); }, globalScene.currentBattle.turn << 4, globalScene.waveSeed);
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
@ -3018,9 +3028,9 @@ export default class BattleScene extends SceneBase {
if (matchingFormChange) { if (matchingFormChange) {
let phase: Phase; let phase: Phase;
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) { if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
phase = new FormChangePhase(this, pokemon, matchingFormChange, modal); phase = new FormChangePhase(pokemon, matchingFormChange, modal);
} else { } else {
phase = new QuietFormChangePhase(this, pokemon, matchingFormChange); phase = new QuietFormChangePhase(pokemon, matchingFormChange);
} }
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) { if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
this.overridePhase(phase); this.overridePhase(phase);
@ -3037,7 +3047,7 @@ export default class BattleScene extends SceneBase {
} }
triggerPokemonBattleAnim(pokemon: Pokemon, battleAnimType: PokemonAnimType, fieldAssets?: Phaser.GameObjects.Sprite[], delayed: boolean = false): boolean { triggerPokemonBattleAnim(pokemon: Pokemon, battleAnimType: PokemonAnimType, fieldAssets?: Phaser.GameObjects.Sprite[], delayed: boolean = false): boolean {
const phase: Phase = new PokemonAnimPhase(this, battleAnimType, pokemon, fieldAssets); const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets);
if (delayed) { if (delayed) {
this.pushPhase(phase); this.pushPhase(phase);
} else { } else {
@ -3055,7 +3065,7 @@ export default class BattleScene extends SceneBase {
validateAchv(achv: Achv, args?: unknown[]): boolean { validateAchv(achv: Achv, args?: unknown[]): boolean {
if ((!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) if ((!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE)
&& achv.validate(this, args)) { && achv.validate(args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
if (vouchers.hasOwnProperty(achv.id)) { if (vouchers.hasOwnProperty(achv.id)) {
@ -3068,7 +3078,7 @@ export default class BattleScene extends SceneBase {
} }
validateVoucher(voucher: Voucher, args?: unknown[]): boolean { validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher); this.ui.achvBar.showAchv(voucher);
this.gameData.voucherCounts[voucher.voucherType]++; this.gameData.voucherCounts[voucher.voucherType]++;
@ -3141,9 +3151,9 @@ export default class BattleScene extends SceneBase {
this.currentBattle.double = true; this.currentBattle.double = true;
const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle()); const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle());
if (availablePartyMembers.length > 1) { if (availablePartyMembers.length > 1) {
this.pushPhase(new ToggleDoublePositionPhase(this, true)); this.pushPhase(new ToggleDoublePositionPhase(true));
if (!availablePartyMembers[1].isOnField()) { if (!availablePartyMembers[1].isOnField()) {
this.pushPhase(new SummonPhase(this, 1)); this.pushPhase(new SummonPhase(1));
} }
} }
@ -3188,7 +3198,7 @@ export default class BattleScene extends SceneBase {
if (participated && pokemonDefeated) { if (participated && pokemonDefeated) {
partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE); partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE);
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier); const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) { if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount()) {
machoBraceModifier.stackCount++; machoBraceModifier.stackCount++;
this.updateModifiers(true, true); this.updateModifiers(true, true);
partyMember.updateInfo(); partyMember.updateInfo();
@ -3250,7 +3260,7 @@ export default class BattleScene extends SceneBase {
if (exp) { if (exp) {
const partyMemberIndex = party.indexOf(expPartyMembers[pm]); const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
this.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(this, partyMemberIndex, exp) : new ShowPartyExpBarPhase(this, partyMemberIndex, exp)); this.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(partyMemberIndex, exp) : new ShowPartyExpBarPhase(partyMemberIndex, exp));
} }
} }
} }
@ -3342,7 +3352,7 @@ export default class BattleScene extends SceneBase {
if (encounter) { if (encounter) {
encounter = new MysteryEncounter(encounter); encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this); encounter.populateDialogueTokensFromRequirements();
return encounter; return encounter;
} }
@ -3370,8 +3380,9 @@ export default class BattleScene extends SceneBase {
} }
let availableEncounters: MysteryEncounter[] = []; let availableEncounters: MysteryEncounter[] = [];
// New encounter should never be the same as the most recent encounter const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ?
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type : null; this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type
: null;
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? []; const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available // If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
while (availableEncounters.length === 0 && tier !== null) { while (availableEncounters.length === 0 && tier !== null) {
@ -3381,27 +3392,27 @@ export default class BattleScene extends SceneBase {
if (!encounterCandidate) { if (!encounterCandidate) {
return false; return false;
} }
if (encounterCandidate.encounterTier !== tier) { // Encounter is in tier if (encounterCandidate.encounterTier !== tier) {
return false; return false;
} }
const disallowedGameModes = encounterCandidate.disallowedGameModes; const disallowedGameModes = encounterCandidate.disallowedGameModes;
if (disallowedGameModes && disallowedGameModes.length > 0 if (disallowedGameModes && disallowedGameModes.length > 0
&& disallowedGameModes.includes(this.gameMode.modeId)) { // Encounter is enabled for game mode && disallowedGameModes.includes(this.gameMode.modeId)) {
return false; return false;
} }
if (this.gameMode.modeId === GameModes.CHALLENGE) { // Encounter is enabled for challenges if (this.gameMode.modeId === GameModes.CHALLENGE) {
const disallowedChallenges = encounterCandidate.disallowedChallenges; const disallowedChallenges = encounterCandidate.disallowedChallenges;
if (disallowedChallenges && disallowedChallenges.length > 0 && this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))) { if (disallowedChallenges && disallowedChallenges.length > 0 && this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))) {
return false; return false;
} }
} }
if (!encounterCandidate.meetsRequirements(this)) { // Meets encounter requirements if (!encounterCandidate.meetsRequirements()) {
return false; return false;
} }
if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one if (previousEncounter !== null && encounterType === previousEncounter) {
return false; return false;
} }
if (this.mysteryEncounterSaveData.encounteredEvents.length > 0 && // Encounter has not exceeded max allowed encounters if (this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0) (encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) { && this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
return false; return false;
@ -3429,7 +3440,7 @@ export default class BattleScene extends SceneBase {
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)]; encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
// New encounter object to not dirty flags // New encounter object to not dirty flags
encounter = new MysteryEncounter(encounter); encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this); encounter.populateDialogueTokensFromRequirements();
return encounter; return encounter;
} }
} }

View File

@ -1,26 +1,27 @@
import BattleScene from "./battle-scene"; import { globalScene } from "#app/global-scene";
import { Command } from "./ui/command-ui-handler"; import type { Command } from "./ui/command-ui-handler";
import * as Utils from "./utils"; import * as Utils from "./utils";
import Trainer, { TrainerVariant } from "./field/trainer"; import Trainer, { TrainerVariant } from "./field/trainer";
import { GameMode } from "./game-mode"; import type { GameMode } from "./game-mode";
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { trainerConfigs } from "#app/data/trainer-config"; import { trainerConfigs } from "#app/data/trainer-config";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { MusicPreference } from "#app/system/settings/settings"; import { MusicPreference } from "#app/system/settings/settings";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { CustomModifierSettings } from "#app/modifier/modifier-type"; import type { CustomModifierSettings } from "#app/modifier/modifier-type";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
export enum ClassicFixedBossWaves { export enum ClassicFixedBossWaves {
// TODO: other fixed wave battles should be added here // TODO: other fixed wave battles should be added here
@ -154,7 +155,7 @@ export default class Battle {
return this.double ? 2 : 1; return this.double ? 2 : 1;
} }
incrementTurn(scene: BattleScene): void { incrementTurn(): void {
this.turn++; this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ])); this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.battleSeedState = null; this.battleSeedState = null;
@ -169,7 +170,7 @@ export default class Battle {
} }
addPostBattleLoot(enemyPokemon: EnemyPokemon): void { addPostBattleLoot(enemyPokemon: EnemyPokemon): void {
this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => { this.postBattleLoot.push(...globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => {
const ret = i as PokemonHeldItemModifier; const ret = i as PokemonHeldItemModifier;
//@ts-ignore - this is awful to fix/change //@ts-ignore - this is awful to fix/change
ret.pokemonId = null; ret.pokemonId = null;
@ -177,43 +178,43 @@ export default class Battle {
})); }));
} }
pickUpScatteredMoney(scene: BattleScene): void { pickUpScatteredMoney(): void {
const moneyAmount = new Utils.IntegerHolder(scene.currentBattle.moneyScattered); const moneyAmount = new Utils.IntegerHolder(globalScene.currentBattle.moneyScattered);
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
if (scene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {
moneyAmount.value *= 2; moneyAmount.value *= 2;
} }
scene.addMoney(moneyAmount.value); globalScene.addMoney(moneyAmount.value);
const userLocale = navigator.language || "en-US"; const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale);
const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount }); const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount });
scene.queueMessage(message, undefined, true); globalScene.queueMessage(message, undefined, true);
scene.currentBattle.moneyScattered = 0; globalScene.currentBattle.moneyScattered = 0;
} }
addBattleScore(scene: BattleScene): void { addBattleScore(): void {
let partyMemberTurnMultiplier = scene.getEnemyParty().length / 2 + 0.5; let partyMemberTurnMultiplier = globalScene.getEnemyParty().length / 2 + 0.5;
if (this.double) { if (this.double) {
partyMemberTurnMultiplier /= 1.5; partyMemberTurnMultiplier /= 1.5;
} }
for (const p of scene.getEnemyParty()) { for (const p of globalScene.getEnemyParty()) {
if (p.isBoss()) { if (p.isBoss()) {
partyMemberTurnMultiplier *= (p.bossSegments / 1.5) / scene.getEnemyParty().length; partyMemberTurnMultiplier *= (p.bossSegments / 1.5) / globalScene.getEnemyParty().length;
} }
} }
const turnMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - Math.min(this.turn - 2, 10 * partyMemberTurnMultiplier) / (10 * partyMemberTurnMultiplier)); const turnMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - Math.min(this.turn - 2, 10 * partyMemberTurnMultiplier) / (10 * partyMemberTurnMultiplier));
const finalBattleScore = Math.ceil(this.battleScore * turnMultiplier); const finalBattleScore = Math.ceil(this.battleScore * turnMultiplier);
scene.score += finalBattleScore; globalScene.score += finalBattleScore;
console.log(`Battle Score: ${finalBattleScore} (${this.turn - 1} Turns x${Math.floor(turnMultiplier * 100) / 100})`); console.log(`Battle Score: ${finalBattleScore} (${this.turn - 1} Turns x${Math.floor(turnMultiplier * 100) / 100})`);
console.log(`Total Score: ${scene.score}`); console.log(`Total Score: ${globalScene.score}`);
scene.updateScoreText(); globalScene.updateScoreText();
} }
getBgmOverride(scene: BattleScene): string | null { getBgmOverride(): string | null {
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
// Music is overridden for MEs during ME onInit() // Music is overridden for MEs during ME onInit()
// Should not use any BGM overrides before swapping from DEFAULT mode // Should not use any BGM overrides before swapping from DEFAULT mode
@ -222,7 +223,7 @@ export default class Battle {
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer?.getEncounterBgm()}`; return `encounter_${this.trainer?.getEncounterBgm()}`;
} }
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
return this.trainer?.getBattleBgm() ?? null; return this.trainer?.getBattleBgm() ?? null;
} else { } else {
return this.trainer?.getMixedBattleBgm() ?? null; return this.trainer?.getMixedBattleBgm() ?? null;
@ -230,7 +231,7 @@ export default class Battle {
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) { } else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
return "end_summit"; return "end_summit";
} }
const wildOpponents = scene.getEnemyParty(); const wildOpponents = globalScene.getEnemyParty();
for (const pokemon of wildOpponents) { for (const pokemon of wildOpponents) {
if (this.battleSpec === BattleSpec.FINAL_BOSS) { if (this.battleSpec === BattleSpec.FINAL_BOSS) {
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) { if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) {
@ -239,7 +240,7 @@ export default class Battle {
return "battle_final_encounter"; return "battle_final_encounter";
} }
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) { if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
switch (pokemon.species.speciesId) { switch (pokemon.species.speciesId) {
case Species.REGIROCK: case Species.REGIROCK:
case Species.REGICE: case Species.REGICE:
@ -256,7 +257,7 @@ export default class Battle {
} }
return "battle_legendary_unova"; return "battle_legendary_unova";
} }
} else if (scene.musicPreference === MusicPreference.ALLGENS) { } else if (globalScene.musicPreference === MusicPreference.ALLGENS) {
switch (pokemon.species.speciesId) { switch (pokemon.species.speciesId) {
case Species.ARTICUNO: case Species.ARTICUNO:
case Species.ZAPDOS: case Species.ZAPDOS:
@ -396,7 +397,7 @@ export default class Battle {
} }
} }
if (scene.gameMode.isClassic && this.waveIndex <= 4) { if (globalScene.gameMode.isClassic && this.waveIndex <= 4) {
return "battle_wild"; return "battle_wild";
} }
@ -409,12 +410,12 @@ export default class Battle {
* @param min The minimum integer to pick, default `0` * @param min The minimum integer to pick, default `0`
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/ */
randSeedInt(scene: BattleScene, range: number, min: number = 0): number { randSeedInt(range: number, min: number = 0): number {
if (range <= 1) { if (range <= 1) {
return min; return min;
} }
const tempRngCounter = scene.rngCounter; const tempRngCounter = globalScene.rngCounter;
const tempSeedOverride = scene.rngSeedOverride; const tempSeedOverride = globalScene.rngSeedOverride;
const state = Phaser.Math.RND.state(); const state = Phaser.Math.RND.state();
if (this.battleSeedState) { if (this.battleSeedState) {
Phaser.Math.RND.state(this.battleSeedState); Phaser.Math.RND.state(this.battleSeedState);
@ -422,13 +423,13 @@ export default class Battle {
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]); Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
console.log("Battle Seed:", this.battleSeed); console.log("Battle Seed:", this.battleSeed);
} }
scene.rngCounter = this.rngCounter++; globalScene.rngCounter = this.rngCounter++;
scene.rngSeedOverride = this.battleSeed; globalScene.rngSeedOverride = this.battleSeed;
const ret = Utils.randSeedInt(range, min); const ret = Utils.randSeedInt(range, min);
this.battleSeedState = Phaser.Math.RND.state(); this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state); Phaser.Math.RND.state(state);
scene.rngCounter = tempRngCounter; globalScene.rngCounter = tempRngCounter;
scene.rngSeedOverride = tempSeedOverride; globalScene.rngSeedOverride = tempSeedOverride;
return ret; return ret;
} }
@ -441,16 +442,16 @@ export default class Battle {
} }
export class FixedBattle extends Battle { export class FixedBattle extends Battle {
constructor(scene: BattleScene, waveIndex: number, config: FixedBattleConfig) { constructor(waveIndex: number, config: FixedBattleConfig) {
super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double); super(globalScene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer() : undefined, config.double);
if (config.getEnemyParty) { if (config.getEnemyParty) {
this.enemyParty = config.getEnemyParty(scene); this.enemyParty = config.getEnemyParty();
} }
} }
} }
type GetTrainerFunc = (scene: BattleScene) => Trainer; type GetTrainerFunc = () => Trainer;
type GetEnemyPartyFunc = (scene: BattleScene) => EnemyPokemon[]; type GetEnemyPartyFunc = () => EnemyPokemon[];
export class FixedBattleConfig { export class FixedBattleConfig {
public battleType: BattleType; public battleType: BattleType;
@ -500,11 +501,11 @@ export class FixedBattleConfig {
* @returns the generated trainer * @returns the generated trainer
*/ */
function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc { function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc {
return (scene: BattleScene) => { return () => {
const rand = Utils.randSeedInt(trainerPool.length); const rand = Utils.randSeedInt(trainerPool.length);
const trainerTypes: TrainerType[] = []; const trainerTypes: TrainerType[] = [];
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) { for (const trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry) const trainerType = Array.isArray(trainerPoolEntry)
? Utils.randSeedItem(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry)
@ -523,10 +524,10 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
return new Trainer(scene, trainerTypes[rand], (Utils.randInt(3) === 0) ? TrainerVariant.DOUBLE : trainerGender); return new Trainer(trainerTypes[rand], (Utils.randInt(3) === 0) ? TrainerVariant.DOUBLE : trainerGender);
} }
return new Trainer(scene, trainerTypes[rand], trainerGender); return new Trainer(trainerTypes[rand], trainerGender);
}; };
} }
@ -544,16 +545,16 @@ export interface FixedBattleConfigs {
*/ */
export const classicFixedBattles: FixedBattleConfigs = { export const classicFixedBattles: FixedBattleConfigs = {
[5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(() => new Trainer(TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_2, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_3, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
@ -562,7 +563,7 @@ export const classicFixedBattles: FixedBattleConfigs = {
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true)), .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true)),
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_4, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
@ -572,7 +573,7 @@ export const classicFixedBattles: FixedBattleConfigs = {
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ])) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_5, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ])) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ]))
@ -588,6 +589,6 @@ export const classicFixedBattles: FixedBattleConfigs = {
[190]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [190]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])),
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_6, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }) .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false })
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
import { Arena } from "#app/field/arena"; import { globalScene } from "#app/global-scene";
import BattleScene from "#app/battle-scene"; import type { Arena } from "#app/field/arena";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils";
import { MoveCategory, allMoves, MoveTarget } from "#app/data/move"; import { MoveCategory, allMoves, MoveTarget } from "#app/data/move";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { HitResult, PokemonMove } from "#app/field/pokemon";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability"; import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
@ -44,7 +45,7 @@ export abstract class ArenaTag {
onRemove(arena: Arena, quiet: boolean = false): void { onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t(`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: this.getMoveName() }));
} }
} }
@ -74,27 +75,25 @@ export abstract class ArenaTag {
/** /**
* Helper function that retrieves the source Pokemon * Helper function that retrieves the source Pokemon
* @param scene medium to retrieve the source Pokemon
* @returns The source {@linkcode Pokemon} or `null` if none is found * @returns The source {@linkcode Pokemon} or `null` if none is found
*/ */
public getSourcePokemon(scene: BattleScene): Pokemon | null { public getSourcePokemon(): Pokemon | null {
return this.sourceId ? scene.getPokemonById(this.sourceId) : null; return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
} }
/** /**
* Helper function that retrieves the Pokemon affected * Helper function that retrieves the Pokemon affected
* @param scene - medium to retrieve the involved Pokemon
* @returns list of PlayerPokemon or EnemyPokemon on the field * @returns list of PlayerPokemon or EnemyPokemon on the field
*/ */
public getAffectedPokemon(scene: BattleScene): Pokemon[] { public getAffectedPokemon(): Pokemon[] {
switch (this.side) { switch (this.side) {
case ArenaTagSide.PLAYER: case ArenaTagSide.PLAYER:
return scene.getPlayerField() ?? []; return globalScene.getPlayerField() ?? [];
case ArenaTagSide.ENEMY: case ArenaTagSide.ENEMY:
return scene.getEnemyField() ?? []; return globalScene.getEnemyField() ?? [];
case ArenaTagSide.BOTH: case ArenaTagSide.BOTH:
default: default:
return scene.getField(true) ?? []; return globalScene.getField(true) ?? [];
} }
} }
} }
@ -112,10 +111,10 @@ export class MistTag extends ArenaTag {
super.onAdd(arena); super.onAdd(arena);
if (this.sourceId) { if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = globalScene.getPokemonById(this.sourceId);
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} else if (!quiet) { } else if (!quiet) {
console.warn("Failed to get source for MistTag onAdd"); console.warn("Failed to get source for MistTag onAdd");
} }
@ -146,7 +145,7 @@ export class MistTag extends ArenaTag {
cancelled.value = true; cancelled.value = true;
if (!simulated) { if (!simulated) {
arena.scene.queueMessage(i18next.t("arenaTag:mistApply")); globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
} }
return true; return true;
@ -193,7 +192,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
if (bypassed.value) { if (bypassed.value) {
return false; return false;
} }
damageMultiplier.value = arena.scene.currentBattle.double ? 2732 / 4096 : 0.5; damageMultiplier.value = globalScene.currentBattle.double ? 2732 / 4096 : 0.5;
return true; return true;
} }
return false; return false;
@ -211,7 +210,7 @@ class ReflectTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -227,7 +226,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -243,7 +242,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -268,7 +267,7 @@ export class ConditionalProtectTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: super.getMoveName() })); globalScene.queueMessage(i18next.t(`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: super.getMoveName() }));
} }
// Removes default message for effect removal // Removes default message for effect removal
@ -296,8 +295,8 @@ export class ConditionalProtectTag extends ArenaTag {
if (!simulated) { if (!simulated) {
attacker.stopMultiHit(defender); attacker.stopMultiHit(defender);
new CommonBattleAnim(CommonAnim.PROTECT, defender).play(arena.scene); new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) })); globalScene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) }));
} }
} }
@ -318,7 +317,7 @@ export class ConditionalProtectTag extends ArenaTag {
*/ */
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
const effectPhase = arena.scene.getCurrentPhase(); const effectPhase = globalScene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase) { if (effectPhase instanceof MoveEffectPhase) {
const attacker = effectPhase.getUserPokemon(); const attacker = effectPhase.getUserPokemon();
@ -393,9 +392,9 @@ class MatBlockTag extends ConditionalProtectTag {
onAdd(arena: Arena) { onAdd(arena: Arena) {
if (this.sourceId) { if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = globalScene.getPokemonById(this.sourceId);
if (source) { if (source) {
arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} else { } else {
console.warn("Failed to get source for MatBlockTag onAdd"); console.warn("Failed to get source for MatBlockTag onAdd");
} }
@ -448,15 +447,15 @@ export class NoCritTag extends ArenaTag {
/** Queues a message upon adding this effect to the field */ /** Queues a message upon adding this effect to the field */
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, { globalScene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
moveName: this.getMoveName() moveName: this.getMoveName()
})); }));
} }
/** Queues a message upon removing this effect from the field */ /** Queues a message upon removing this effect from the field */
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
const source = arena.scene.getPokemonById(this.sourceId!); // TODO: is this bang correct? const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", { globalScene.queueMessage(i18next.t("arenaTag:noCritOnRemove", {
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
moveName: this.getMoveName() moveName: this.getMoveName()
})); }));
@ -478,7 +477,7 @@ class WishTag extends ArenaTag {
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
if (this.sourceId) { if (this.sourceId) {
const user = arena.scene.getPokemonById(this.sourceId); const user = globalScene.getPokemonById(this.sourceId);
if (user) { if (user) {
this.battlerIndex = user.getBattlerIndex(); this.battlerIndex = user.getBattlerIndex();
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
@ -490,10 +489,10 @@ class WishTag extends ArenaTag {
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
const target = arena.scene.getField()[this.battlerIndex]; const target = globalScene.getField()[this.battlerIndex];
if (target?.isActive(true)) { if (target?.isActive(true)) {
arena.scene.queueMessage(this.triggerMessage); globalScene.queueMessage(this.triggerMessage);
arena.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), this.healHp, null, true, false)); globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
} }
} }
} }
@ -546,11 +545,11 @@ class MudSportTag extends WeakenMoveTypeTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:mudSportOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:mudSportOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
} }
} }
@ -564,11 +563,11 @@ class WaterSportTag extends WeakenMoveTypeTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:waterSportOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:waterSportOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
} }
} }
@ -584,7 +583,7 @@ export class IonDelugeTag extends ArenaTag {
/** Queues an on-add message */ /** Queues an on-add message */
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
} }
onRemove(arena: Arena): void { } // Removes default on-remove message onRemove(arena: Arena): void { } // Removes default on-remove message
@ -679,9 +678,9 @@ class SpikesTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -698,7 +697,7 @@ class SpikesTag extends ArenaTrapTag {
const damageHpRatio = 1 / (10 - 2 * this.layers); const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) { if (pokemon.turnData) {
pokemon.turnData.damageTaken += damage; pokemon.turnData.damageTaken += damage;
@ -728,9 +727,9 @@ class ToxicSpikesTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -747,8 +746,8 @@ class ToxicSpikesTag extends ArenaTrapTag {
} }
if (pokemon.isOfType(Type.POISON)) { if (pokemon.isOfType(Type.POISON)) {
this.neutralized = true; this.neutralized = true;
if (pokemon.scene.arena.removeTag(this.tagType)) { if (globalScene.arena.removeTag(this.tagType)) {
pokemon.scene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
return true; return true;
} }
} else if (!pokemon.status) { } else if (!pokemon.status) {
@ -792,7 +791,7 @@ export class DelayedAttackTag extends ArenaTag {
const ret = super.lapse(arena); const ret = super.lapse(arena);
if (!ret) { if (!ret) {
arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct? globalScene.unshiftPhase(new MoveEffectPhase(this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct?
} }
return ret; return ret;
@ -814,9 +813,9 @@ class StealthRockTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -864,7 +863,7 @@ class StealthRockTag extends ArenaTrapTag {
return true; return true;
} }
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) { if (pokemon.turnData) {
pokemon.turnData.damageTaken += damage; pokemon.turnData.damageTaken += damage;
@ -893,9 +892,9 @@ class StickyWebTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -909,9 +908,9 @@ class StickyWebTag extends ArenaTrapTag {
} }
if (!cancelled.value) { if (!cancelled.value) {
pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() })); globalScene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
const stages = new NumberHolder(-1); const stages = new NumberHolder(-1);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value));
return true; return true;
} }
} }
@ -945,14 +944,14 @@ export class TrickRoomTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (source) { if (source) {
arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} }
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
} }
} }
@ -967,8 +966,8 @@ export class GravityTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
arena.scene.getField(true).forEach((pokemon) => { globalScene.getField(true).forEach((pokemon) => {
if (pokemon !== null) { if (pokemon !== null) {
pokemon.removeTag(BattlerTagType.FLOATING); pokemon.removeTag(BattlerTagType.FLOATING);
pokemon.removeTag(BattlerTagType.TELEKINESIS); pokemon.removeTag(BattlerTagType.TELEKINESIS);
@ -980,7 +979,7 @@ export class GravityTag extends ArenaTag {
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
} }
} }
@ -996,29 +995,29 @@ class TailwindTag extends ArenaTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
const source = arena.scene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
const party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? []; const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
for (const pokemon of party) { for (const pokemon of party) {
// Apply the CHARGED tag to party members with the WIND_POWER ability // Apply the CHARGED tag to party members with the WIND_POWER ability
if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
pokemon.addTag(BattlerTagType.CHARGED); pokemon.addTag(BattlerTagType.CHARGED);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
} }
// Raise attack by one stage if party member has WIND_RIDER ability // Raise attack by one stage if party member has WIND_RIDER ability
if (pokemon.hasAbility(Abilities.WIND_RIDER)) { if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex())); globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex()));
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true));
} }
} }
} }
onRemove(arena: Arena, quiet: boolean = false): void { onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -1033,11 +1032,11 @@ class HappyHourTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:happyHourOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:happyHourOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
} }
} }
@ -1047,11 +1046,11 @@ class SafeguardTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
@ -1074,16 +1073,16 @@ class ImprisonTag extends ArenaTrapTag {
* This function applies the effects of Imprison to the opposing Pokemon already present on the field. * This function applies the effects of Imprison to the opposing Pokemon already present on the field.
* @param arena * @param arena
*/ */
override onAdd({ scene }: Arena) { override onAdd() {
const source = this.getSourcePokemon(scene); const source = this.getSourcePokemon();
if (source) { if (source) {
const party = this.getAffectedPokemon(scene); const party = this.getAffectedPokemon();
party?.forEach((p: Pokemon ) => { party?.forEach((p: Pokemon ) => {
if (p.isAllowedInBattle()) { if (p.isAllowedInBattle()) {
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
} }
}); });
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} }
} }
@ -1092,8 +1091,8 @@ class ImprisonTag extends ArenaTrapTag {
* @param _arena * @param _arena
* @returns `true` if the source of the tag is still active on the field | `false` if not * @returns `true` if the source of the tag is still active on the field | `false` if not
*/ */
override lapse({ scene }: Arena): boolean { override lapse(): boolean {
const source = this.getSourcePokemon(scene); const source = this.getSourcePokemon();
return source ? source.isActive(true) : false; return source ? source.isActive(true) : false;
} }
@ -1103,7 +1102,7 @@ class ImprisonTag extends ArenaTrapTag {
* @returns `true` * @returns `true`
*/ */
override activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon): boolean {
const source = this.getSourcePokemon(pokemon.scene); const source = this.getSourcePokemon();
if (source && source.isActive(true) && pokemon.isAllowedInBattle()) { if (source && source.isActive(true) && pokemon.isAllowedInBattle()) {
pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
} }
@ -1114,8 +1113,8 @@ class ImprisonTag extends ArenaTrapTag {
* When the arena tag is removed, it also attempts to remove any related Battler Tags if they haven't already been removed from the affected Pokemon * When the arena tag is removed, it also attempts to remove any related Battler Tags if they haven't already been removed from the affected Pokemon
* @param arena * @param arena
*/ */
override onRemove({ scene }: Arena): void { override onRemove(): void {
const party = this.getAffectedPokemon(scene); const party = this.getAffectedPokemon();
party?.forEach((p: Pokemon) => { party?.forEach((p: Pokemon) => {
p.removeTag(BattlerTagType.IMPRISON); p.removeTag(BattlerTagType.IMPRISON);
}); });
@ -1136,19 +1135,19 @@ class FireGrassPledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A sea of fire enveloped your/the opposing team!" // "A sea of fire enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
override lapse(arena: Arena): boolean { override lapse(arena: Arena): boolean {
const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER) const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER)
? arena.scene.getPlayerField() ? globalScene.getPlayerField()
: arena.scene.getEnemyField(); : globalScene.getEnemyField();
field.filter(pokemon => !pokemon.isOfType(Type.FIRE) && !pokemon.switchOutStatus).forEach(pokemon => { field.filter(pokemon => !pokemon.isOfType(Type.FIRE) && !pokemon.switchOutStatus).forEach(pokemon => {
// "{pokemonNameWithAffix} was hurt by the sea of fire!" // "{pokemonNameWithAffix} was hurt by the sea of fire!"
pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
// TODO: Replace this with a proper animation // TODO: Replace this with a proper animation
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM)); globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM));
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8)); pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
}); });
@ -1170,7 +1169,7 @@ class WaterFirePledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A rainbow appeared in the sky on your/the opposing team's side!" // "A rainbow appeared in the sky on your/the opposing team's side!"
arena.scene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
/** /**
@ -1200,7 +1199,7 @@ class GrassWaterPledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A swamp enveloped your/the opposing team!" // "A swamp enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
@ -1217,7 +1216,7 @@ export class FairyLockTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
} }
} }

View File

@ -1,6 +1,7 @@
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { pokemonEvolutions, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import i18next from "i18next"; import i18next from "i18next";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Species } from "#enums/species"; import { Species } from "#enums/species";

View File

@ -1,6 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
@ -266,8 +267,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ELECTRODE, 30, null, null) new SpeciesEvolution(Species.ELECTRODE, 30, null, null)
], ],
[Species.CUBONE]: [ [Species.CUBONE]: [
new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.TYROGUE]: [ [Species.TYROGUE]: [
/** /**
@ -287,8 +288,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
)), )),
], ],
[Species.KOFFING]: [ [Species.KOFFING]: [
new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.RHYHORN]: [ [Species.RHYHORN]: [
new SpeciesEvolution(Species.RHYDON, 42, null, null) new SpeciesEvolution(Species.RHYDON, 42, null, null)
@ -333,8 +334,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.QUILAVA, 14, null, null) new SpeciesEvolution(Species.QUILAVA, 14, null, null)
], ],
[Species.QUILAVA]: [ [Species.QUILAVA]: [
new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.TOTODILE]: [ [Species.TOTODILE]: [
new SpeciesEvolution(Species.CROCONAW, 18, null, null) new SpeciesEvolution(Species.CROCONAW, 18, null, null)
@ -436,8 +437,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LINOONE, 20, null, null) new SpeciesEvolution(Species.LINOONE, 20, null, null)
], ],
[Species.WURMPLE]: [ [Species.WURMPLE]: [
new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY)),
new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.SILCOON]: [ [Species.SILCOON]: [
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null) new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
@ -478,7 +479,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
], ],
[Species.NINCADA]: [ [Species.NINCADA]: [
new SpeciesEvolution(Species.NINJASK, 20, null, null), new SpeciesEvolution(Species.NINJASK, 20, null, null),
new SpeciesEvolution(Species.SHEDINJA, 20, null, new SpeciesEvolutionCondition(p => p.scene.getPlayerParty().length < 6 && p.scene.pokeballCounts[PokeballType.POKEBALL] > 0)) new SpeciesEvolution(Species.SHEDINJA, 20, null, new SpeciesEvolutionCondition(p => globalScene.getPlayerParty().length < 6 && globalScene.pokeballCounts[PokeballType.POKEBALL] > 0))
], ],
[Species.WHISMUR]: [ [Species.WHISMUR]: [
new SpeciesEvolution(Species.LOUDRED, 20, null, null) new SpeciesEvolution(Species.LOUDRED, 20, null, null)
@ -660,7 +661,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LUMINEON, 31, null, null) new SpeciesEvolution(Species.LUMINEON, 31, null, null)
], ],
[Species.MANTYKE]: [ [Species.MANTYKE]: [
new SpeciesEvolution(Species.MANTINE, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.REMORAID].caughtAttr), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.MANTINE, 32, null, new SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.REMORAID].caughtAttr), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.SNOVER]: [ [Species.SNOVER]: [
new SpeciesEvolution(Species.ABOMASNOW, 40, null, null) new SpeciesEvolution(Species.ABOMASNOW, 40, null, null)
@ -681,8 +682,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DEWOTT, 17, null, null) new SpeciesEvolution(Species.DEWOTT, 17, null, null)
], ],
[Species.DEWOTT]: [ [Species.DEWOTT]: [
new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.PATRAT]: [ [Species.PATRAT]: [
new SpeciesEvolution(Species.WATCHOG, 20, null, null) new SpeciesEvolution(Species.WATCHOG, 20, null, null)
@ -832,8 +833,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.RUFFLET]: [ [Species.RUFFLET]: [
new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.VULLABY]: [ [Species.VULLABY]: [
new SpeciesEvolution(Species.MANDIBUZZ, 54, null, null) new SpeciesEvolution(Species.MANDIBUZZ, 54, null, null)
@ -890,7 +891,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GOGOAT, 32, null, null) new SpeciesEvolution(Species.GOGOAT, 32, null, null)
], ],
[Species.PANCHAM]: [ [Species.PANCHAM]: [
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!globalScene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.ESPURR]: [ [Species.ESPURR]: [
new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)), new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)),
@ -912,21 +913,21 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CLAWITZER, 37, null, null) new SpeciesEvolution(Species.CLAWITZER, 37, null, null)
], ],
[Species.TYRUNT]: [ [Species.TYRUNT]: [
new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.AMAURA]: [ [Species.AMAURA]: [
new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.GOOMY]: [ [Species.GOOMY]: [
new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.SLIGGOO]: [ [Species.SLIGGOO]: [
new SpeciesEvolution(Species.GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.BERGMITE]: [ [Species.BERGMITE]: [
new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.NOIBAT]: [ [Species.NOIBAT]: [
new SpeciesEvolution(Species.NOIVERN, 48, null, null) new SpeciesEvolution(Species.NOIVERN, 48, null, null)
@ -935,8 +936,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DARTRIX, 17, null, null) new SpeciesEvolution(Species.DARTRIX, 17, null, null)
], ],
[Species.DARTRIX]: [ [Species.DARTRIX]: [
new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.LITTEN]: [ [Species.LITTEN]: [
new SpeciesEvolution(Species.TORRACAT, 17, null, null) new SpeciesEvolution(Species.TORRACAT, 17, null, null)
@ -957,7 +958,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TOUCANNON, 28, null, null) new SpeciesEvolution(Species.TOUCANNON, 28, null, null)
], ],
[Species.YUNGOOS]: [ [Species.YUNGOOS]: [
new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.GRUBBIN]: [ [Species.GRUBBIN]: [
new SpeciesEvolution(Species.CHARJABUG, 20, null, null) new SpeciesEvolution(Species.CHARJABUG, 20, null, null)
@ -975,7 +976,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ARAQUANID, 22, null, null) new SpeciesEvolution(Species.ARAQUANID, 22, null, null)
], ],
[Species.FOMANTIS]: [ [Species.FOMANTIS]: [
new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
], ],
[Species.MORELULL]: [ [Species.MORELULL]: [
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
@ -1012,7 +1013,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.MELMETAL, 48, null, null) new SpeciesEvolution(Species.MELMETAL, 48, null, null)
], ],
[Species.ALOLA_RATTATA]: [ [Species.ALOLA_RATTATA]: [
new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.ALOLA_DIGLETT]: [ [Species.ALOLA_DIGLETT]: [
new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null) new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null)
@ -1135,7 +1136,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null) new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null)
], ],
[Species.GALAR_LINOONE]: [ [Species.GALAR_LINOONE]: [
new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.GALAR_YAMASK]: [ [Species.GALAR_YAMASK]: [
new SpeciesEvolution(Species.RUNERIGUS, 34, null, null) new SpeciesEvolution(Species.RUNERIGUS, 34, null, null)
@ -1144,7 +1145,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HISUI_ZOROARK, 30, null, null) new SpeciesEvolution(Species.HISUI_ZOROARK, 30, null, null)
], ],
[Species.HISUI_SLIGGOO]: [ [Species.HISUI_SLIGGOO]: [
new SpeciesEvolution(Species.HISUI_GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.HISUI_GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SPRIGATITO]: [ [Species.SPRIGATITO]: [
new SpeciesEvolution(Species.FLORAGATO, 16, null, null) new SpeciesEvolution(Species.FLORAGATO, 16, null, null)
@ -1183,7 +1184,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
[Species.TANDEMAUS]: [ [Species.TANDEMAUS]: [
new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => {
let ret = false; let ret = false;
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
return ret; return ret;
})), })),
new SpeciesEvolution(Species.MAUSHOLD, 25, null, null) new SpeciesEvolution(Species.MAUSHOLD, 25, null, null)
@ -1243,7 +1244,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GLIMMORA, 35, null, null) new SpeciesEvolution(Species.GLIMMORA, 35, null, null)
], ],
[Species.GREAVARD]: [ [Species.GREAVARD]: [
new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.FRIGIBAX]: [ [Species.FRIGIBAX]: [
new SpeciesEvolution(Species.ARCTIBAX, 35, null, null) new SpeciesEvolution(Species.ARCTIBAX, 35, null, null)
@ -1311,10 +1312,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
[Species.EEVEE]: [ [Species.EEVEE]: [
new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG),
@ -1351,17 +1352,17 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => {
let ret = false; let ret = false;
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) { if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) {
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
} }
return ret; return ret;
}), SpeciesWildEvolutionDelay.LONG), }), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.GLIGAR]: [ [Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SNEASEL]: [ [Species.SNEASEL]: [
new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.URSARING]: [ [Species.URSARING]: [
new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
@ -1391,8 +1392,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.MIME_JR]: [ [Species.MIME_JR]: [
new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM), new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.PANSAGE]: [ [Species.PANSAGE]: [
new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1442,9 +1443,9 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ROCKRUFF]: [ [Species.ROCKRUFF]: [
new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))), new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))),
new SpeciesFormEvolution(Species.LYCANROC, "own-tempo", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)), new SpeciesFormEvolution(Species.LYCANROC, "own-tempo", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)),
new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0))) new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)))
], ],
[Species.STEENEE]: [ [Species.STEENEE]: [
new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG)
@ -1471,15 +1472,15 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesFormEvolution(Species.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG) new SpeciesFormEvolution(Species.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.MILCERY]: [ [Species.MILCERY]: [
new SpeciesFormEvolution(Species.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.TOWN || p.scene.arena.biomeType === Biome.PLAINS || p.scene.arena.biomeType === Biome.GRASS || p.scene.arena.biomeType === Biome.TALL_GRASS || p.scene.arena.biomeType === Biome.METROPOLIS), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.TOWN || globalScene.arena.biomeType === Biome.PLAINS || globalScene.arena.biomeType === Biome.GRASS || globalScene.arena.biomeType === Biome.TALL_GRASS || globalScene.arena.biomeType === Biome.METROPOLIS), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.BADLANDS || p.scene.arena.biomeType === Biome.VOLCANO || p.scene.arena.biomeType === Biome.GRAVEYARD || p.scene.arena.biomeType === Biome.FACTORY || p.scene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.BADLANDS || globalScene.arena.biomeType === Biome.VOLCANO || globalScene.arena.biomeType === Biome.GRAVEYARD || globalScene.arena.biomeType === Biome.FACTORY || globalScene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.FOREST || p.scene.arena.biomeType === Biome.SWAMP || p.scene.arena.biomeType === Biome.MEADOW || p.scene.arena.biomeType === Biome.JUNGLE), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.FOREST || globalScene.arena.biomeType === Biome.SWAMP || globalScene.arena.biomeType === Biome.MEADOW || globalScene.arena.biomeType === Biome.JUNGLE), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.SEA || p.scene.arena.biomeType === Biome.BEACH || p.scene.arena.biomeType === Biome.LAKE || p.scene.arena.biomeType === Biome.SEABED), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.SEA || globalScene.arena.biomeType === Biome.BEACH || globalScene.arena.biomeType === Biome.LAKE || globalScene.arena.biomeType === Biome.SEABED), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.DESERT || p.scene.arena.biomeType === Biome.POWER_PLANT || p.scene.arena.biomeType === Biome.DOJO || p.scene.arena.biomeType === Biome.RUINS || p.scene.arena.biomeType === Biome.CONSTRUCTION_SITE), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.DESERT || globalScene.arena.biomeType === Biome.POWER_PLANT || globalScene.arena.biomeType === Biome.DOJO || globalScene.arena.biomeType === Biome.RUINS || globalScene.arena.biomeType === Biome.CONSTRUCTION_SITE), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.MOUNTAIN || p.scene.arena.biomeType === Biome.CAVE || p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.FAIRY_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.MOUNTAIN || globalScene.arena.biomeType === Biome.CAVE || globalScene.arena.biomeType === Biome.ICE_CAVE || globalScene.arena.biomeType === Biome.FAIRY_CAVE || globalScene.arena.biomeType === Biome.SNOWY_FOREST), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.WASTELAND || p.scene.arena.biomeType === Biome.LABORATORY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.WASTELAND || globalScene.arena.biomeType === Biome.LABORATORY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.TEMPLE || p.scene.arena.biomeType === Biome.ISLAND), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.TEMPLE || globalScene.arena.biomeType === Biome.ISLAND), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ABYSS || p.scene.arena.biomeType === Biome.SPACE || p.scene.arena.biomeType === Biome.END), SpeciesWildEvolutionDelay.LONG) new SpeciesFormEvolution(Species.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.ABYSS || globalScene.arena.biomeType === Biome.SPACE || globalScene.arena.biomeType === Biome.END), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DURALUDON]: [ [Species.DURALUDON]: [
new SpeciesFormEvolution(Species.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesFormEvolution(Species.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1501,7 +1502,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_SNEASEL]: [ [Species.HISUI_SNEASEL]: [
new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.CHARCADET]: [ [Species.CHARCADET]: [
new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG), new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG),
@ -1581,10 +1582,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.KARRABLAST]: [ [Species.KARRABLAST]: [
new SpeciesEvolution(Species.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.SHELMET].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.SHELMET].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SHELMET]: [ [Species.SHELMET]: [
new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SPRITZEE]: [ [Species.SPRITZEE]: [
new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1627,13 +1628,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.MARILL, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.MARILL, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.BUDEW]: [ [Species.BUDEW]: [
new SpeciesEvolution(Species.ROSELIA, 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.ROSELIA, 1, null, new SpeciesFriendshipEvolutionCondition(70, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.BUNEARY]: [ [Species.BUNEARY]: [
new SpeciesEvolution(Species.LOPUNNY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.LOPUNNY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.CHINGLING]: [ [Species.CHINGLING]: [
new SpeciesEvolution(Species.CHIMECHO, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.CHIMECHO, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.HAPPINY]: [ [Species.HAPPINY]: [
new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT)
@ -1642,7 +1643,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.RIOLU]: [ [Species.RIOLU]: [
new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.WOOBAT]: [ [Species.WOOBAT]: [
new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM)
@ -1657,16 +1658,16 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SNOM]: [ [Species.SNOM]: [
new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.GIMMIGHOUL]: [ [Species.GIMMIGHOUL]: [
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier + globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG), || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length + p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier + globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG) || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG)
] ]
}; };

View File

@ -1,16 +1,14 @@
//import { battleAnimRawData } from "./battle-anim-raw-data"; import { globalScene } from "#app/global-scene";
import BattleScene from "../battle-scene";
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move"; import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move";
import Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import type { BattlerIndex } from "../battle";
import { Element } from "json-stable-stringify"; import type { Element } from "json-stable-stringify";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { SubstituteTag } from "./battler-tags"; import { SubstituteTag } from "./battler-tags";
import { isNullOrUndefined } from "../utils"; import { isNullOrUndefined } from "../utils";
import Phaser from "phaser"; import Phaser from "phaser";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
//import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget { export enum AnimFrameTarget {
USER, USER,
@ -308,7 +306,7 @@ abstract class AnimTimedEvent {
this.resourceName = resourceName; this.resourceName = resourceName;
} }
abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer; abstract execute(battleAnim: BattleAnim, priority?: number): integer;
abstract getEventType(): string; abstract getEventType(): string;
} }
@ -326,15 +324,15 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
} }
} }
execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer { execute(battleAnim: BattleAnim, priority?: number): integer {
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) }; const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
if (this.resourceName) { if (this.resourceName) {
try { try {
scene.playSound(`battle_anims/${this.resourceName}`, soundConfig); globalScene.playSound(`battle_anims/${this.resourceName}`, soundConfig);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
return Math.ceil((scene.sound.get(`battle_anims/${this.resourceName}`).totalDuration * 1000) / 33.33); return Math.ceil((globalScene.sound.get(`battle_anims/${this.resourceName}`).totalDuration * 1000) / 33.33);
} else { } else {
return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct? return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct?
} }
@ -388,7 +386,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { execute(moveAnim: MoveAnim, priority?: number): integer {
const tweenProps = {}; const tweenProps = {};
if (this.bgX !== undefined) { if (this.bgX !== undefined) {
tweenProps["x"] = (this.bgX * 0.5) - 320; tweenProps["x"] = (this.bgX * 0.5) - 320;
@ -400,7 +398,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
tweenProps["alpha"] = (this.opacity || 0) / 255; tweenProps["alpha"] = (this.opacity || 0) / 255;
} }
if (Object.keys(tweenProps).length) { if (Object.keys(tweenProps).length) {
scene.tweens.add(Object.assign({ globalScene.tweens.add(Object.assign({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: Utils.getFrameMs(this.duration * 3)
}, tweenProps)); }, tweenProps));
@ -418,25 +416,25 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { execute(moveAnim: MoveAnim, priority?: number): integer {
if (moveAnim.bgSprite) { if (moveAnim.bgSprite) {
moveAnim.bgSprite.destroy(); moveAnim.bgSprite.destroy();
} }
moveAnim.bgSprite = this.resourceName moveAnim.bgSprite = this.resourceName
? scene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName) ? globalScene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName)
: scene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0); : globalScene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0);
moveAnim.bgSprite.setOrigin(0, 0); moveAnim.bgSprite.setOrigin(0, 0);
moveAnim.bgSprite.setScale(1.25); moveAnim.bgSprite.setScale(1.25);
moveAnim.bgSprite.setAlpha(this.opacity / 255); moveAnim.bgSprite.setAlpha(this.opacity / 255);
scene.field.add(moveAnim.bgSprite); globalScene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false); const fieldPokemon = globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false);
if (!isNullOrUndefined(priority)) { if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority); globalScene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
} else if (fieldPokemon?.isOnField()) { } else if (fieldPokemon?.isOnField()) {
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); globalScene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
} }
scene.tweens.add({ globalScene.tweens.add({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: Utils.getFrameMs(this.duration * 3)
}); });
@ -454,14 +452,14 @@ export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimCon
export const commonAnims = new Map<CommonAnim, AnimConfig>(); export const commonAnims = new Map<CommonAnim, AnimConfig>();
export const encounterAnims = new Map<EncounterAnim, AnimConfig>(); export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
export function initCommonAnims(scene: BattleScene): Promise<void> { export function initCommonAnims(): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimNames = Utils.getEnumKeys(CommonAnim);
const commonAnimIds = Utils.getEnumValues(CommonAnim); const commonAnimIds = Utils.getEnumValues(CommonAnim);
const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = []; const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
for (let ca = 0; ca < commonAnimIds.length; ca++) { for (let ca = 0; ca < commonAnimIds.length; ca++) {
const commonAnimId = commonAnimIds[ca]; const commonAnimId = commonAnimIds[ca];
commonAnimFetches.push(scene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) commonAnimFetches.push(globalScene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas)))); .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))));
} }
@ -469,7 +467,7 @@ export function initCommonAnims(scene: BattleScene): Promise<void> {
}); });
} }
export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> { export function initMoveAnim(move: Moves): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
if (moveAnims.has(move)) { if (moveAnims.has(move)) {
if (moveAnims.get(move) !== null) { if (moveAnims.get(move) !== null) {
@ -494,7 +492,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP; const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
const fetchAnimAndResolve = (move: Moves) => { const fetchAnimAndResolve = (move: Moves) => {
scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`) globalScene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
.then(response => { .then(response => {
const contentType = response.headers.get("content-type"); const contentType = response.headers.get("content-type");
if (!response.ok || contentType?.indexOf("application/json") === -1) { if (!response.ok || contentType?.indexOf("application/json") === -1) {
@ -516,7 +514,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] : (allMoves[move].getAttrs(DelayedAttackAttr)[0]
?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]); ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
if (chargeAnimSource) { if (chargeAnimSource) {
initMoveChargeAnim(scene, chargeAnimSource.chargeAnim).then(() => resolve()); initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
} else { } else {
resolve(); resolve();
} }
@ -557,10 +555,9 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
/** /**
* Fetches animation configs to be used in a Mystery Encounter * Fetches animation configs to be used in a Mystery Encounter
* @param scene
* @param encounterAnim one or more animations to fetch * @param encounterAnim one or more animations to fetch
*/ */
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> { export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ]; const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ];
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = []; const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
@ -568,14 +565,14 @@ export async function initEncounterAnims(scene: BattleScene, encounterAnim: Enco
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
continue; continue;
} }
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) encounterAnimFetches.push(globalScene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => encounterAnims.set(anim, new AnimConfig(cas)))); .then(cas => encounterAnims.set(anim, new AnimConfig(cas))));
} }
await Promise.allSettled(encounterAnimFetches); await Promise.allSettled(encounterAnimFetches);
} }
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> { export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
if (chargeAnims.has(chargeAnim)) { if (chargeAnims.has(chargeAnim)) {
if (chargeAnims.get(chargeAnim) !== null) { if (chargeAnims.get(chargeAnim) !== null) {
@ -590,7 +587,7 @@ export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim):
} }
} else { } else {
chargeAnims.set(chargeAnim, null); chargeAnims.set(chargeAnim, null);
scene.cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`) globalScene.cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(ca => { .then(ca => {
if (Array.isArray(ca)) { if (Array.isArray(ca)) {
@ -623,23 +620,22 @@ function populateMoveChargeAnim(chargeAnim: ChargeAnim, animSource: any) {
chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]); chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]);
} }
export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> { export function loadCommonAnimAssets(startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
loadAnimAssets(scene, Array.from(commonAnims.values()), startLoad).then(() => resolve()); loadAnimAssets(Array.from(commonAnims.values()), startLoad).then(() => resolve());
}); });
} }
/** /**
* Loads encounter animation assets to scene * Loads encounter animation assets to scene
* MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly * MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly
* @param scene
* @param startLoad * @param startLoad
*/ */
export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> { export async function loadEncounterAnimAssets(startLoad?: boolean): Promise<void> {
await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad); await loadAnimAssets(Array.from(encounterAnims.values()), startLoad);
} }
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> { export function loadMoveAnimAssets(moveIds: Moves[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat(); const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
for (const moveId of moveIds) { for (const moveId of moveIds) {
@ -655,11 +651,11 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo
} }
} }
} }
loadAnimAssets(scene, moveAnimations, startLoad).then(() => resolve()); loadAnimAssets(moveAnimations, startLoad).then(() => resolve());
}); });
} }
function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boolean): Promise<void> { function loadAnimAssets(anims: AnimConfig[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const backgrounds = new Set<string>(); const backgrounds = new Set<string>();
const sounds = new Set<string>(); const sounds = new Set<string>();
@ -676,19 +672,19 @@ function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boo
backgrounds.add(abg); backgrounds.add(abg);
} }
if (a.graphic) { if (a.graphic) {
scene.loadSpritesheet(a.graphic, "battle_anims", 96); globalScene.loadSpritesheet(a.graphic, "battle_anims", 96);
} }
} }
for (const bg of backgrounds) { for (const bg of backgrounds) {
scene.loadImage(bg, "battle_anims"); globalScene.loadImage(bg, "battle_anims");
} }
for (const s of sounds) { for (const s of sounds) {
scene.loadSe(s, "battle_anims", s); globalScene.loadSe(s, "battle_anims", s);
} }
if (startLoad) { if (startLoad) {
scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve()); globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
if (!scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
scene.load.start(); globalScene.load.start();
} }
} else { } else {
resolve(); resolve();
@ -778,7 +774,7 @@ export abstract class BattleAnim {
return false; return false;
} }
private getGraphicFrameData(scene: BattleScene, frames: AnimFrame[], onSubstitute?: boolean): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> { private getGraphicFrameData(frames: AnimFrame[], onSubstitute?: boolean): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([ const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ], [ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ], [ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
@ -835,7 +831,7 @@ export abstract class BattleAnim {
return ret; return ret;
} }
play(scene: BattleScene, onSubstitute?: boolean, callback?: Function) { play(onSubstitute?: boolean, callback?: Function) {
const isOppAnim = this.isOppAnim(); const isOppAnim = this.isOppAnim();
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct? const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
const target = !isOppAnim ? this.target! : this.user!; const target = !isOppAnim ? this.target! : this.user!;
@ -907,7 +903,7 @@ export abstract class BattleAnim {
} }
}; };
if (!scene.moveAnimations && !this.playRegardlessOfIssues) { if (!globalScene.moveAnimations && !this.playRegardlessOfIssues) {
return cleanUpAndComplete(); return cleanUpAndComplete();
} }
@ -924,7 +920,7 @@ export abstract class BattleAnim {
let r = anim?.frames.length ?? 0; let r = anim?.frames.length ?? 0;
let f = 0; let f = 0;
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3), duration: Utils.getFrameMs(3),
repeat: anim?.frames.length ?? 0, repeat: anim?.frames.length ?? 0,
onRepeat: () => { onRepeat: () => {
@ -934,7 +930,7 @@ export abstract class BattleAnim {
} }
const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT? const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT?
const frameData = this.getGraphicFrameData(scene, anim!.frames[f], onSubstitute); // TODO: is the bang correct? const frameData = this.getGraphicFrameData(anim!.frames[f], onSubstitute); // TODO: is the bang correct?
let u = 0; let u = 0;
let t = 0; let t = 0;
let g = 0; let g = 0;
@ -950,19 +946,19 @@ export abstract class BattleAnim {
const spriteSource = isUser ? userSprite : targetSprite; const spriteSource = isUser ? userSprite : targetSprite;
if ((isUser ? u : t) === sprites.length) { if ((isUser ? u : t) === sprites.length) {
if (isUser || !targetSubstitute) { if (isUser || !targetSubstitute) {
const sprite = scene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct? const sprite = globalScene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct?
[ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct? [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct?
sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey()); sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey());
sprite.setPipelineData("shiny", (isUser ? user : target).shiny); sprite.setPipelineData("shiny", (isUser ? user : target).shiny);
sprite.setPipelineData("variant", (isUser ? user : target).variant); sprite.setPipelineData("variant", (isUser ? user : target).variant);
sprite.setPipelineData("ignoreFieldPos", true); sprite.setPipelineData("ignoreFieldPos", true);
spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame)); spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame));
scene.field.add(sprite); globalScene.field.add(sprite);
sprites.push(sprite); sprites.push(sprite);
} else { } else {
const sprite = scene.addFieldSprite(spriteSource.x, spriteSource.y, spriteSource.texture); const sprite = globalScene.addFieldSprite(spriteSource.x, spriteSource.y, spriteSource.texture);
spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame)); spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame));
scene.field.add(sprite); globalScene.field.add(sprite);
sprites.push(sprite); sprites.push(sprite);
} }
} }
@ -987,9 +983,9 @@ export abstract class BattleAnim {
} else { } else {
const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (g === sprites.length) { if (g === sprites.length) {
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct? const newSprite: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct?
sprites.push(newSprite); sprites.push(newSprite);
scene.field.add(newSprite); globalScene.field.add(newSprite);
spritePriorities.push(1); spritePriorities.push(1);
} }
@ -1000,22 +996,22 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
switch (priority) { switch (priority) {
case 0: case 0:
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false)!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false)!); // TODO: is this bang correct?
break; break;
case 1: case 1:
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
break; break;
case 2: case 2:
switch (frame.focus) { switch (frame.focus) {
case AnimFocus.USER: case AnimFocus.USER:
if (this.bgSprite) { if (this.bgSprite) {
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite);
} else { } else {
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct?
} }
break; break;
case AnimFocus.TARGET: case AnimFocus.TARGET:
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct?
break; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -1025,10 +1021,10 @@ export abstract class BattleAnim {
case 3: case 3:
switch (frame.focus) { switch (frame.focus) {
case AnimFocus.USER: case AnimFocus.USER:
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct?
break; break;
case AnimFocus.TARGET: case AnimFocus.TARGET:
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct?
break; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -1056,7 +1052,7 @@ export abstract class BattleAnim {
} }
if (anim?.frameTimedEvents.has(f)) { if (anim?.frameTimedEvents.has(f)) {
for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct? for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct?
r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); r = Math.max((anim.frames.length - f) + event.execute(this), r);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = Utils.getEnumValues(AnimFrameTarget);
@ -1086,7 +1082,7 @@ export abstract class BattleAnim {
} }
} }
if (r) { if (r) {
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(r), duration: Utils.getFrameMs(r),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
@ -1123,8 +1119,6 @@ export abstract class BattleAnim {
} }
/** /**
*
* @param scene
* @param targetInitialX * @param targetInitialX
* @param targetInitialY * @param targetInitialY
* @param frameTimeMult * @param frameTimeMult
@ -1135,7 +1129,7 @@ export abstract class BattleAnim {
* - 5 is on top of player sprite * - 5 is on top of player sprite
* @param callback * @param callback
*/ */
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) { playWithoutTargets(targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) {
const spriteCache: SpriteCache = { const spriteCache: SpriteCache = {
[AnimFrameTarget.GRAPHIC]: [], [AnimFrameTarget.GRAPHIC]: [],
[AnimFrameTarget.USER]: [], [AnimFrameTarget.USER]: [],
@ -1156,7 +1150,7 @@ export abstract class BattleAnim {
} }
}; };
if (!scene.moveAnimations && !this.playRegardlessOfIssues) { if (!globalScene.moveAnimations && !this.playRegardlessOfIssues) {
return cleanUpAndComplete(); return cleanUpAndComplete();
} }
@ -1168,13 +1162,13 @@ export abstract class BattleAnim {
let totalFrames = anim!.frames.length; let totalFrames = anim!.frames.length;
let frameCount = 0; let frameCount = 0;
let existingFieldSprites = scene.field.getAll().slice(0); let existingFieldSprites = globalScene.field.getAll().slice(0);
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3) * frameTimeMult, duration: Utils.getFrameMs(3) * frameTimeMult,
repeat: anim!.frames.length, repeat: anim!.frames.length,
onRepeat: () => { onRepeat: () => {
existingFieldSprites = scene.field.getAll().slice(0); existingFieldSprites = globalScene.field.getAll().slice(0);
const spriteFrames = anim!.frames[frameCount]; const spriteFrames = anim!.frames[frameCount];
const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY); const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY);
let graphicFrameCount = 0; let graphicFrameCount = 0;
@ -1186,9 +1180,9 @@ export abstract class BattleAnim {
const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (graphicFrameCount === sprites.length) { if (graphicFrameCount === sprites.length) {
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); const newSprite: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(0, 0, anim!.graphic, 1);
sprites.push(newSprite); sprites.push(newSprite);
scene.field.add(newSprite); globalScene.field.add(newSprite);
} }
const graphicIndex = graphicFrameCount++; const graphicIndex = graphicFrameCount++;
@ -1197,11 +1191,11 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
if (existingFieldSprites.length > priority) { if (existingFieldSprites.length > priority) {
// Move to specified priority index // Move to specified priority index
const index = scene.field.getIndex(existingFieldSprites[priority]); const index = globalScene.field.getIndex(existingFieldSprites[priority]);
scene.field.moveTo(moveSprite, index); globalScene.field.moveTo(moveSprite, index);
} else { } else {
// Move to top of scene // Move to top of scene
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
} }
}; };
setSpritePriority(frame.priority); setSpritePriority(frame.priority);
@ -1221,7 +1215,7 @@ export abstract class BattleAnim {
} }
if (anim?.frameTimedEvents.get(frameCount)) { if (anim?.frameTimedEvents.get(frameCount)) {
for (const event of anim.frameTimedEvents.get(frameCount)!) { for (const event of anim.frameTimedEvents.get(frameCount)!) {
totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(scene, this, frameTimedEventPriority), totalFrames); totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(this, frameTimedEventPriority), totalFrames);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = Utils.getEnumValues(AnimFrameTarget);
@ -1248,7 +1242,7 @@ export abstract class BattleAnim {
} }
} }
if (totalFrames) { if (totalFrames) {
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(totalFrames), duration: Utils.getFrameMs(totalFrames),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
@ -1282,7 +1276,7 @@ export class MoveAnim extends BattleAnim {
public move: Moves; public move: Moves;
constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField: boolean = false) { constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField: boolean = false) {
super(user, user.scene.getField()[target], playOnEmptyField); super(user, globalScene.getField()[target], playOnEmptyField);
this.move = move; this.move = move;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import Pokemon, { HitResult } from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { HitResult } from "../field/pokemon";
import { getStatusEffectHealText } from "./status-effect"; import { getStatusEffectHealText } from "./status-effect";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs } from "./ability"; import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs } from "./ability";
@ -9,6 +10,7 @@ import { BerryType } from "#enums/berry-type";
import { Stat, type BattleStat } from "#app/enums/stat"; import { Stat, type BattleStat } from "#app/enums/stat";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { globalScene } from "#app/global-scene";
export function getBerryName(berryType: BerryType): string { export function getBerryName(berryType: BerryType): string {
return i18next.t(`berry:${BerryType[berryType]}.name`); return i18next.t(`berry:${BerryType[berryType]}.name`);
@ -73,7 +75,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
} }
const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4)); const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(),
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true)); hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
@ -83,7 +85,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
pokemon.battleData.berriesEaten.push(berryType); pokemon.battleData.berriesEaten.push(berryType);
} }
if (pokemon.status) { if (pokemon.status) {
pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); globalScene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
} }
pokemon.resetStatus(true, true); pokemon.resetStatus(true, true);
pokemon.updateInfo(); pokemon.updateInfo();
@ -102,7 +104,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const stat: BattleStat = berryType - BerryType.ENIGMA; const stat: BattleStat = berryType - BerryType.ENIGMA;
const statStages = new Utils.NumberHolder(1); const statStages = new Utils.NumberHolder(1);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], statStages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
case BerryType.LANSAT: case BerryType.LANSAT:
@ -121,7 +123,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK); const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK);
const stages = new Utils.NumberHolder(2); const stages = new Utils.NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ randStat ], stages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
case BerryType.LEPPA: case BerryType.LEPPA:
@ -132,7 +134,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct? const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct?
if (ppRestoreMove !== undefined) { if (ppRestoreMove !== undefined) {
ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0); ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) })); globalScene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) }));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
} }
}; };

View File

@ -1,18 +1,22 @@
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data"; import type { DexAttrProps, GameData } from "#app/system/game-data";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { defaultStarterSpecies } from "#app/system/game-data";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { BattleType, FixedBattleConfig } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon";
import type { FixedBattleConfig } from "#app/battle";
import { BattleType } from "#app/battle";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import { GameMode } from "#app/game-mode"; import type { GameMode } from "#app/game-mode";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { TypeColor, TypeShadow } from "#enums/color"; import { TypeColor, TypeShadow } from "#enums/color";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { pokemonFormChanges } from "#app/data/pokemon-forms"; import { pokemonFormChanges } from "#app/data/pokemon-forms";
@ -467,7 +471,7 @@ export class SingleGenerationChallenge extends Challenge {
if (trainerTypes.length === 0) { if (trainerTypes.length === 0) {
return false; return false;
} else { } else {
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT)); battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(() => new Trainer(trainerTypes[this.value - 1], TrainerVariant.DEFAULT));
return true; return true;
} }
} }

View File

@ -1,7 +1,7 @@
import { Abilities } from "#enums/abilities"; import type { Abilities } from "#enums/abilities";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
/** /**
* Data that can customize a Pokemon in non-standard ways from its Species * Data that can customize a Pokemon in non-standard ways from its Species

View File

@ -1,10 +1,11 @@
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { Species } from "#enums/species"; import type { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PlayerPokemon } from "#app/field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
import { Starter } from "#app/ui/starter-select-ui-handler"; import type { Starter } from "#app/ui/starter-select-ui-handler";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import PokemonSpecies, { PokemonSpeciesForm, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
@ -21,17 +22,17 @@ export function fetchDailyRunSeed(): Promise<string | null> {
}); });
} }
export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[] { export function getDailyRunStarters(seed: string): Starter[] {
const starters: Starter[] = []; const starters: Starter[] = [];
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const startingLevel = scene.gameMode.getStartingLevel(); const startingLevel = globalScene.gameMode.getStartingLevel();
if (/\d{18}$/.test(seed)) { if (/\d{18}$/.test(seed)) {
for (let s = 0; s < 3; s++) { for (let s = 0; s < 3; s++) {
const offset = 6 + s * 6; const offset = 6 + s * 6;
const starterSpeciesForm = getPokemonSpeciesForm(parseInt(seed.slice(offset, offset + 4)) as Species, parseInt(seed.slice(offset + 4, offset + 6))); const starterSpeciesForm = getPokemonSpeciesForm(parseInt(seed.slice(offset, offset + 4)) as Species, parseInt(seed.slice(offset + 4, offset + 6)));
starters.push(getDailyRunStarter(scene, starterSpeciesForm, startingLevel)); starters.push(getDailyRunStarter(starterSpeciesForm, startingLevel));
} }
return; return;
} }
@ -48,17 +49,17 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
.filter(s => speciesStarterCosts[s] === cost); .filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies)); const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER)); const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER));
starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel)); starters.push(getDailyRunStarter(starterSpecies, startingLevel));
} }
}, 0, seed); }, 0, seed);
return starters; return starters;
} }
function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeciesForm, startingLevel: integer): Starter { function getDailyRunStarter(starterSpeciesForm: PokemonSpeciesForm, startingLevel: integer): Starter {
const starterSpecies = starterSpeciesForm instanceof PokemonSpecies ? starterSpeciesForm : getPokemonSpecies(starterSpeciesForm.speciesId); const starterSpecies = starterSpeciesForm instanceof PokemonSpecies ? starterSpeciesForm : getPokemonSpecies(starterSpeciesForm.speciesId);
const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex; const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex;
const pokemon = new PlayerPokemon(scene, starterSpecies, startingLevel, undefined, formIndex, undefined, undefined, undefined, undefined, undefined, undefined); const pokemon = new PlayerPokemon(starterSpecies, startingLevel, undefined, formIndex, undefined, undefined, undefined, undefined, undefined, undefined);
const starter: Starter = { const starter: Starter = {
species: starterSpecies, species: starterSpecies,
dexAttr: pokemon.getDexAttr(), dexAttr: pokemon.getDexAttr(),

View File

@ -1,6 +1,6 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { DexEntry, StarterDataEntry } from "#app/system/game-data"; import type { DexEntry, StarterDataEntry } from "#app/system/game-data";
/** /**
* Stores data associated with a specific egg and the hatched pokemon * Stores data associated with a specific egg and the hatched pokemon
@ -17,11 +17,8 @@ export class EggHatchData {
public dexEntryBeforeUpdate: DexEntry; public dexEntryBeforeUpdate: DexEntry;
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */ /** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
public starterDataEntryBeforeUpdate: StarterDataEntry; public starterDataEntryBeforeUpdate: StarterDataEntry;
/** reference to the battle scene to get gamedata and update dex */
private scene: BattleScene;
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) { constructor(pokemon: PlayerPokemon, eggMoveIndex: number) {
this.scene = scene;
this.pokemon = pokemon; this.pokemon = pokemon;
this.eggMoveIndex = eggMoveIndex; this.eggMoveIndex = eggMoveIndex;
} }
@ -39,8 +36,8 @@ export class EggHatchData {
* Used before updating the dex, so comparing the pokemon to these entries will show the new attributes * Used before updating the dex, so comparing the pokemon to these entries will show the new attributes
*/ */
setDex() { setDex() {
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId]; const currDexEntry = globalScene.gameData.dexData[this.pokemon.species.speciesId];
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()]; const currStarterDataEntry = globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
this.dexEntryBeforeUpdate = { this.dexEntryBeforeUpdate = {
seenAttr: currDexEntry.seenAttr, seenAttr: currDexEntry.seenAttr,
caughtAttr: currDexEntry.caughtAttr, caughtAttr: currDexEntry.caughtAttr,
@ -86,9 +83,9 @@ export class EggHatchData {
*/ */
updatePokemon(showMessage : boolean = false) { updatePokemon(showMessage : boolean = false) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => { globalScene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => { globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
this.setEggMoveUnlocked(value); this.setEggMoveUnlocked(value);
resolve(); resolve();
}); });

View File

@ -1,11 +1,13 @@
import BattleScene from "#app/battle-scene"; import type BattleScene from "#app/battle-scene";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import { globalScene } from "#app/global-scene";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { VariantTier } from "#enums/variant-tier"; import { VariantTier } from "#enums/variant-tier";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -22,9 +24,8 @@ export interface IEggOptions {
/** Timestamp when this egg got created */ /** Timestamp when this egg got created */
timestamp?: number; timestamp?: number;
/** /**
* Defines if the egg got pulled from a gacha or not. If true, egg pity and pull statistics will be applyed. * Defines if the egg got pulled from a gacha or not. If true, egg pity and pull statistics will be applied.
* Egg will be automaticly added to the game data. * Egg will be automaticly added to the game data.
* NEEDS `scene` `eggOption` to work.
*/ */
pulled?: boolean; pulled?: boolean;
/** /**
@ -32,7 +33,7 @@ export interface IEggOptions {
* Will also define the text displayed in the egg list. * Will also define the text displayed in the egg list.
*/ */
sourceType?: EggSourceType; sourceType?: EggSourceType;
/** Needs to be defined if `eggOption` pulled is defined or if no species or `isShiny` is defined since this will be needed to generate them. */ /** Legacy field, kept for backwards-compatibility */
scene?: BattleScene; scene?: BattleScene;
/** /**
* Sets the tier of the egg. Only species of this tier can be hatched from this egg. * Sets the tier of the egg. Only species of this tier can be hatched from this egg.
@ -41,10 +42,7 @@ export interface IEggOptions {
tier?: EggTier; tier?: EggTier;
/** Sets how many waves it will take till this egg hatches. */ /** Sets how many waves it will take till this egg hatches. */
hatchWaves?: number; hatchWaves?: number;
/** /** Sets the exact species that will hatch from this egg. */
* Sets the exact species that will hatch from this egg.
* Needs `scene` `eggOption` if not provided.
*/
species?: Species; species?: Species;
/** Defines if the hatched pokemon will be a shiny. */ /** Defines if the hatched pokemon will be a shiny. */
isShiny?: boolean; isShiny?: boolean;
@ -56,8 +54,7 @@ export interface IEggOptions {
* Defines if the egg will hatch with the hidden ability of this species. * Defines if the egg will hatch with the hidden ability of this species.
* If no hidden ability exist, a random one will get choosen. * If no hidden ability exist, a random one will get choosen.
*/ */
overrideHiddenAbility?: boolean, overrideHiddenAbility?: boolean;
/** Can customize the message displayed for where the egg was obtained */ /** Can customize the message displayed for where the egg was obtained */
eggDescriptor?: string; eggDescriptor?: string;
} }
@ -148,7 +145,7 @@ export class Egg {
// If egg was pulled, check if egg pity needs to override the egg tier // If egg was pulled, check if egg pity needs to override the egg tier
if (eggOptions?.pulled) { if (eggOptions?.pulled) {
// Needs this._tier and this._sourceType to work // Needs this._tier and this._sourceType to work
this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? this.checkForPityTierOverrides();
} }
this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier);
@ -160,7 +157,7 @@ export class Egg {
// First roll shiny and variant so we can filter if species with an variant exist // First roll shiny and variant so we can filter if species with an variant exist
this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny());
this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant());
this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? this._species = eggOptions?.species ?? this.rollSpecies()!; // TODO: Is this bang correct?
this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false;
@ -178,19 +175,15 @@ export class Egg {
// Needs this._tier so it needs to be generated afer the tier override if bought from same species // Needs this._tier so it needs to be generated afer the tier override if bought from same species
this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
if (eggOptions?.pulled) { if (eggOptions?.pulled) {
this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? this.increasePullStatistic();
this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? this.addEggToGameData();
} }
}; };
if (eggOptions?.scene) { const seedOverride = Utils.randomString(24);
const seedOverride = Utils.randomString(24); globalScene.executeWithSeedOffset(() => {
eggOptions?.scene.executeWithSeedOffset(() => {
generateEggProperties(eggOptions);
}, 0, seedOverride);
} else { // For legacy eggs without scene
generateEggProperties(eggOptions); generateEggProperties(eggOptions);
} }, 0, seedOverride);
this._eggDescriptor = eggOptions?.eggDescriptor; this._eggDescriptor = eggOptions?.eggDescriptor;
} }
@ -212,14 +205,14 @@ export class Egg {
} }
// Generates a PlayerPokemon from an egg // Generates a PlayerPokemon from an egg
public generatePlayerPokemon(scene: BattleScene): PlayerPokemon { public generatePlayerPokemon(): PlayerPokemon {
let ret: PlayerPokemon; let ret: PlayerPokemon;
const generatePlayerPokemonHelper = (scene: BattleScene) => { const generatePlayerPokemonHelper = () => {
// Legacy egg wants to hatch. Generate missing properties // Legacy egg wants to hatch. Generate missing properties
if (!this._species) { if (!this._species) {
this._isShiny = this.rollShiny(); this._isShiny = this.rollShiny();
this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? this._species = this.rollSpecies()!; // TODO: is this bang correct?
} }
let pokemonSpecies = getPokemonSpecies(this._species); let pokemonSpecies = getPokemonSpecies(this._species);
@ -238,7 +231,7 @@ export class Egg {
} }
// This function has way to many optional parameters // This function has way to many optional parameters
ret = scene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false); ret = globalScene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false);
ret.shiny = this._isShiny; ret.shiny = this._isShiny;
ret.variant = this._variantTier; ret.variant = this._variantTier;
@ -250,16 +243,16 @@ export class Egg {
}; };
ret = ret!; // Tell TS compiler it's defined now ret = ret!; // Tell TS compiler it's defined now
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
generatePlayerPokemonHelper(scene); generatePlayerPokemonHelper();
}, this._id, EGG_SEED.toString()); }, this._id, EGG_SEED.toString());
return ret; return ret;
} }
// Doesn't need to be called if the egg got pulled by a gacha machiene // Doesn't need to be called if the egg got pulled by a gacha machiene
public addEggToGameData(scene: BattleScene): void { public addEggToGameData(): void {
scene.gameData.eggs.push(this); globalScene.gameData.eggs.push(this);
} }
public getEggDescriptor(): string { public getEggDescriptor(): string {
@ -291,12 +284,12 @@ export class Egg {
return i18next.t("egg:hatchWavesMessageLongTime"); return i18next.t("egg:hatchWavesMessageLongTime");
} }
public getEggTypeDescriptor(scene: BattleScene): string { public getEggTypeDescriptor(): string {
switch (this.sourceType) { switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG: case EggSourceType.SAME_SPECIES_EGG:
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() }); return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() });
case EggSourceType.GACHA_LEGENDARY: case EggSourceType.GACHA_LEGENDARY:
return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`; return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`;
case EggSourceType.GACHA_SHINY: case EggSourceType.GACHA_SHINY:
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny"); return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
case EggSourceType.GACHA_MOVE: case EggSourceType.GACHA_MOVE:
@ -356,8 +349,8 @@ export class Egg {
return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY; return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY;
} }
private rollSpecies(scene: BattleScene): Species | null { private rollSpecies(): Species | null {
if (!scene) { if (!globalScene) {
return null; return null;
} }
/** /**
@ -376,7 +369,7 @@ export class Egg {
} else if (this.tier === EggTier.LEGENDARY } else if (this.tier === EggTier.LEGENDARY
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) { && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!Utils.randSeedInt(2)) { if (!Utils.randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp); return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
} }
} }
@ -410,8 +403,8 @@ export class Egg {
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
// If this is the 10th egg without unlocking something new, attempt to force it. // If this is the 10th egg without unlocking something new, attempt to force it.
if (scene.gameData.unlockPity[this.tier] >= 9) { if (globalScene.gameData.unlockPity[this.tier] >= 9) {
const lockedPool = speciesPool.filter(s => !scene.gameData.dexData[s].caughtAttr && !scene.gameData.eggs.some(e => e.species === s)); const lockedPool = speciesPool.filter(s => !globalScene.gameData.dexData[s].caughtAttr && !globalScene.gameData.eggs.some(e => e.species === s));
if (lockedPool.length) { // Skip this if everything is unlocked if (lockedPool.length) { // Skip this if everything is unlocked
speciesPool = lockedPool; speciesPool = lockedPool;
} }
@ -454,10 +447,10 @@ export class Egg {
} }
species = species!; // tell TS compiled it's defined now! species = species!; // tell TS compiled it's defined now!
if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) { if (globalScene.gameData.dexData[species].caughtAttr || globalScene.gameData.eggs.some(e => e.species === species)) {
scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10); globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
} else { } else {
scene.gameData.unlockPity[this.tier] = 0; globalScene.gameData.unlockPity[this.tier] = 0;
} }
return species; return species;
@ -465,7 +458,7 @@ export class Egg {
/** /**
* Rolls whether the egg is shiny or not. * Rolls whether the egg is shiny or not.
* @returns True if the egg is shiny * @returns `true` if the egg is shiny
**/ **/
private rollShiny(): boolean { private rollShiny(): boolean {
let shinyChance = GACHA_DEFAULT_SHINY_RATE; let shinyChance = GACHA_DEFAULT_SHINY_RATE;
@ -485,6 +478,7 @@ export class Egg {
// Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one // Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one
// place but I don't want to touch the pokemon class. // place but I don't want to touch the pokemon class.
// TODO: Remove this or replace the one in the Pokemon class.
private rollVariant(): VariantTier { private rollVariant(): VariantTier {
if (!this.isShiny) { if (!this.isShiny) {
return VariantTier.STANDARD; return VariantTier.STANDARD;
@ -500,38 +494,38 @@ export class Egg {
} }
} }
private checkForPityTierOverrides(scene: BattleScene): void { private checkForPityTierOverrides(): void {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
scene.gameData.eggPity[EggTier.RARE] += 1; globalScene.gameData.eggPity[EggTier.RARE] += 1;
scene.gameData.eggPity[EggTier.EPIC] += 1; globalScene.gameData.eggPity[EggTier.EPIC] += 1;
scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset; globalScene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { if (globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.LEGENDARY; this._tier = EggTier.LEGENDARY;
} else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { } else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.EPIC; this._tier = EggTier.EPIC;
} else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { } else if (globalScene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.RARE; this._tier = EggTier.RARE;
} }
scene.gameData.eggPity[this._tier] = 0; globalScene.gameData.eggPity[this._tier] = 0;
} }
private increasePullStatistic(scene: BattleScene): void { private increasePullStatistic(): void {
scene.gameData.gameStats.eggsPulled++; globalScene.gameData.gameStats.eggsPulled++;
if (this.isManaphyEgg()) { if (this.isManaphyEgg()) {
scene.gameData.gameStats.manaphyEggsPulled++; globalScene.gameData.gameStats.manaphyEggsPulled++;
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC); this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC);
return; return;
} }
switch (this.tier) { switch (this.tier) {
case EggTier.RARE: case EggTier.RARE:
scene.gameData.gameStats.rareEggsPulled++; globalScene.gameData.gameStats.rareEggsPulled++;
break; break;
case EggTier.EPIC: case EggTier.EPIC:
scene.gameData.gameStats.epicEggsPulled++; globalScene.gameData.gameStats.epicEggsPulled++;
break; break;
case EggTier.LEGENDARY: case EggTier.LEGENDARY:
scene.gameData.gameStats.legendaryEggsPulled++; globalScene.gameData.gameStats.legendaryEggsPulled++;
break; break;
} }
} }
@ -552,7 +546,7 @@ export function getValidLegendaryGachaSpecies() : Species[] {
.filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS); .filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS);
} }
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species { export function getLegendaryGachaSpeciesForTimestamp(timestamp: number): Species {
const legendarySpecies = getValidLegendaryGachaSpecies(); const legendarySpecies = getValidLegendaryGachaSpecies();
let ret: Species; let ret: Species;
@ -563,7 +557,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
const offset = Math.floor(Math.floor(dayTimestamp / 86400000) / legendarySpecies.length); // Cycle number const offset = Math.floor(Math.floor(dayTimestamp / 86400000) / legendarySpecies.length); // Cycle number
const index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle const index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.shuffle(legendarySpecies)[index]; ret = Phaser.Math.RND.shuffle(legendarySpecies)[index];
}, offset, EGG_SEED.toString()); }, offset, EGG_SEED.toString());
ret = ret!; // tell TS compiler it's ret = ret!; // tell TS compiler it's

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,17 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { globalScene } from "#app/global-scene";
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, } from "#app/data/trainer-config"; import { trainerConfigs, } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
import { IEggOptions } from "#app/data/egg"; import type { IEggOptions } from "#app/data/egg";
import { EggSourceType } from "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase";
@ -36,8 +38,8 @@ export const ATrainersTestEncounter: MysteryEncounter =
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Randomly pick from 1 of the 5 stat trainers to spawn // Randomly pick from 1 of the 5 stat trainers to spawn
let trainerType: TrainerType; let trainerType: TrainerType;
@ -138,23 +140,22 @@ export const ATrainersTestEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip` buttonTooltip: `${namespace}:option.1.tooltip`
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Battle the stat trainer for an Egg and great rewards // Battle the stat trainer for an Egg and great rewards
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
const eggOptions: IEggOptions = { const eggOptions: IEggOptions = {
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.EPIC tier: EggTier.EPIC
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -162,21 +163,20 @@ export const ATrainersTestEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Full heal party // Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true)); globalScene.unshiftPhase(new PartyHealPhase(true));
const eggOptions: IEggOptions = { const eggOptions: IEggOptions = {
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.RARE tier: EggTier.RARE
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`));
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]); setEncounterRewards({ fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withOutroDialogue([ .withOutroDialogue([

View File

@ -1,10 +1,14 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -19,8 +23,8 @@ import { BattlerIndex } from "#app/battle";
import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { BerryType } from "#enums/berry-type"; import type { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -170,18 +174,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav"); globalScene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3"); globalScene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
// Sort berries by party member ID to more easily re-add later if necessary // Sort berries by party member ID to more easily re-add later if necessary
const berryItemsMap = new Map<number, BerryModifier[]>(); const berryItemsMap = new Map<number, BerryModifier[]>();
scene.getPlayerParty().forEach(pokemon => { globalScene.getPlayerParty().forEach(pokemon => {
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id); const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
if (pokemonBerries?.length > 0) { if (pokemonBerries?.length > 0) {
berryItemsMap.set(pokemon.id, pokemonBerries); berryItemsMap.set(pokemon.id, pokemonBerries);
@ -196,7 +200,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Can't define stack count on a ModifierType, have to just create separate instances for each stack // Can't define stack count on a ModifierType, have to just create separate instances for each stack
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway // Overflow berries will be "lost" on the boss, but it's un-catchable anyway
for (let i = 0; i < berryMod.stackCount; i++) { for (let i = 0; i < berryMod.stackCount; i++) {
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [ berryMod.berryType ]) as PokemonHeldItemModifierType; const modifierType = generateModifierType(modifierTypes.BERRY, [ berryMod.berryType ]) as PokemonHeldItemModifierType;
bossModifierConfigs.push({ modifier: modifierType }); bossModifierConfigs.push({ modifier: modifierType });
} }
}); });
@ -204,7 +208,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Do NOT remove the real berries yet or else it will be persisted in the session data // Do NOT remove the real berries yet or else it will be persisted in the session data
// SpDef buff below wave 50, +1 to all stats otherwise // SpDef buff below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.SPDEF ] : [ Stat.SPDEF ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
@ -221,8 +225,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
modifierConfigs: bossModifierConfigs, modifierConfigs: bossModifierConfigs,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`); queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
} }
], ],
@ -233,18 +237,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
doGreedentSpriteSteal(scene); doGreedentSpriteSteal();
doBerrySpritePile(scene); doBerrySpritePile();
// Remove the berries from the party // Remove the berries from the party
// Session has been safely saved at this point, so data won't be lost // Session has been safely saved at this point, so data won't be lost
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
berryItems.forEach(berryMod => { berryItems.forEach(berryMod => {
scene.removeModifier(berryMod); globalScene.removeModifier(berryMod);
}); });
scene.updateModifiers(true); globalScene.updateModifiers(true);
return true; return true;
}) })
@ -260,26 +264,26 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Provides 1x Reviver Seed to each party member at end of battle // Provides 1x Reviver Seed to each party member at end of battle
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED); const revSeed = generateModifierType(modifierTypes.REVIVER_SEED);
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
const givePartyPokemonReviverSeeds = () => { const givePartyPokemonReviverSeeds = () => {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
party.forEach(p => { party.forEach(p => {
const heldItems = p.getHeldItems(); const heldItems = p.getHeldItems();
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) { if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
const seedModifier = revSeed.newModifier(p); const seedModifier = revSeed.newModifier(p);
scene.addModifier(seedModifier, false, false, false, true); globalScene.addModifier(seedModifier, false, false, false, true);
} }
}); });
queueEncounterMessage(scene, `${namespace}:option.1.food_stash`); queueEncounterMessage(`${namespace}:option.1.food_stash`);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds); setEncounterRewards({ fillRemaining: true }, undefined, givePartyPokemonReviverSeeds);
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [ BattlerIndex.ENEMY ], targets: [ BattlerIndex.ENEMY ],
@ -287,8 +291,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
) )
@ -304,12 +308,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const berryMap = encounter.misc.berryItemsMap; const berryMap = encounter.misc.berryItemsMap;
// Returns 2/5 of the berries stolen to each Pokemon // Returns 2/5 of the berries stolen to each Pokemon
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
party.forEach(pokemon => { party.forEach(pokemon => {
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id); const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
const berryTypesAsArray: BerryType[] = []; const berryTypesAsArray: BerryType[] = [];
@ -322,15 +326,15 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
Phaser.Math.RND.shuffle(berryTypesAsArray); Phaser.Math.RND.shuffle(berryTypesAsArray);
const randBerryType = berryTypesAsArray.pop(); const randBerryType = berryTypesAsArray.pop();
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType; const berryModType = generateModifierType(modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType;
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType); applyModifierTypeToPlayerPokemon(pokemon, berryModType);
} }
} }
}); });
await scene.updateModifiers(true); await globalScene.updateModifiers(true);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -346,36 +350,36 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Animate berries being eaten // Animate berries being eaten
doGreedentEatBerries(scene); doGreedentEatBerries();
doBerrySpritePile(scene, true); doBerrySpritePile(true);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Let it have the food // Let it have the food
// Greedent joins the team, level equal to 2 below highest party member (shiny locked) // Greedent joins the team, level equal to 2 below highest party member (shiny locked)
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2; const level = getHighestLevelPlayerPokemon(false, true).level - 2;
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true); const greedent = new EnemyPokemon(getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true);
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ]; greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
greedent.passive = true; greedent.passive = true;
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false); await catchPokemon(greedent, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
.build(); .build();
function doGreedentSpriteSteal(scene: BattleScene) { function doGreedentSpriteSteal() {
const shakeDelay = 50; const shakeDelay = 50;
const slideDelay = 500; const slideDelay = 500;
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1); const greedentSprites = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
scene.playSound("battle_anims/Follow Me"); globalScene.playSound("battle_anims/Follow Me");
scene.tweens.chain({ globalScene.tweens.chain({
targets: greedentSprites, targets: greedentSprites,
tweens: [ tweens: [
{ // Slide Greedent diagonally { // Slide Greedent diagonally
@ -445,10 +449,10 @@ function doGreedentSpriteSteal(scene: BattleScene) {
}); });
} }
function doGreedentEatBerries(scene: BattleScene) { function doGreedentEatBerries() {
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1); const greedentSprites = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
let index = 1; let index = 1;
scene.tweens.add({ globalScene.tweens.add({
targets: greedentSprites, targets: greedentSprites,
duration: 150, duration: 150,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
@ -456,11 +460,11 @@ function doGreedentEatBerries(scene: BattleScene) {
y: "-=8", y: "-=8",
loop: 5, loop: 5,
onStart: () => { onStart: () => {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
}, },
onLoop: () => { onLoop: () => {
if (index % 2 === 0) { if (index % 2 === 0) {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
} }
index++; index++;
} }
@ -468,17 +472,15 @@ function doGreedentEatBerries(scene: BattleScene) {
} }
/** /**
*
* @param scene
* @param isEat Default false. Will "create" pile when false, and remove pile when true. * @param isEat Default false. Will "create" pile when false, and remove pile when true.
*/ */
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) { function doBerrySpritePile(isEat: boolean = false) {
const berryAddDelay = 150; const berryAddDelay = 150;
let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ]; let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ];
if (isEat) { if (isEat) {
animationOrder = animationOrder.reverse(); animationOrder = animationOrder.reverse();
} }
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
animationOrder.forEach((berry, i) => { animationOrder.forEach((berry, i) => {
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry)); const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite; let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
@ -487,7 +489,7 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
sprite = sprites[0]; sprite = sprites[0];
tintSprite = sprites[1]; tintSprite = sprites[1];
} }
scene.time.delayedCall(berryAddDelay * i + 400, () => { globalScene.time.delayedCall(berryAddDelay * i + 400, () => {
if (sprite) { if (sprite) {
sprite.setVisible(!isEat); sprite.setVisible(!isEat);
} }
@ -497,20 +499,20 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
// Animate Petaya berry falling off the pile // Animate Petaya berry falling off the pile
if (berry === "petaya" && sprite && tintSprite && !isEat) { if (berry === "petaya" && sprite && tintSprite && !isEat) {
scene.time.delayedCall(200, () => { globalScene.time.delayedCall(200, () => {
doBerryBounce(scene, [ sprite, tintSprite ], 30, 500); doBerryBounce([ sprite, tintSprite ], 30, 500);
}); });
} }
}); });
}); });
} }
function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: number) { function doBerryBounce(berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: number) {
let bouncePower = 1; let bouncePower = 1;
let bounceYOffset = yd; let bounceYOffset = yd;
const doBounce = () => { const doBounce = () => {
scene.tweens.add({ globalScene.tweens.add({
targets: berrySprites, targets: berrySprites,
y: "+=" + bounceYOffset, y: "+=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
@ -522,7 +524,7 @@ function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Spri
if (bouncePower) { if (bouncePower) {
bounceYOffset = bounceYOffset * bouncePower; bounceYOffset = bounceYOffset * bouncePower;
scene.tweens.add({ globalScene.tweens.add({
targets: berrySprites, targets: berrySprites,
y: "-=" + bounceYOffset, y: "-=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },

View File

@ -2,8 +2,9 @@ import { generateModifierType, leaveEncounterWithoutBattle, setEncounterExp, upd
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -69,14 +70,14 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); const pokemon = getHighestStatTotalPlayerPokemon(true, true);
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(); const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId();
const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1; const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1;
const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER); const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER);
const price = scene.getWaveMoneyAmount(multiplier); const price = globalScene.getWaveMoneyAmount(multiplier);
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
@ -89,7 +90,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
// If player meets the combo OR requirements for option 2, populate the token // If player meets the combo OR requirements for option 2, populate the token
const opt2Req = encounter.options[1].primaryPokemonRequirements[0]; const opt2Req = encounter.options[1].primaryPokemonRequirements[0];
if (opt2Req.meetsRequirement(scene)) { if (opt2Req.meetsRequirement()) {
const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"]; const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"];
const moveToken = encounter.dialogueTokens["option2PrimaryMove"]; const moveToken = encounter.dialogueTokens["option2PrimaryMove"];
if (abilityToken) { if (abilityToken) {
@ -99,7 +100,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
} }
} }
const shinyCharm = generateModifierType(scene, modifierTypes.SHINY_CHARM); const shinyCharm = generateModifierType(modifierTypes.SHINY_CHARM);
encounter.setDialogueToken("itemName", shinyCharm?.name ?? i18next.t("modifierType:ModifierType.SHINY_CHARM.name")); encounter.setDialogueToken("itemName", shinyCharm?.name ?? i18next.t("modifierType:ModifierType.SHINY_CHARM.name"));
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName()); encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
@ -118,17 +119,17 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party // Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price); updatePlayerMoney(encounter.misc.price);
scene.removePokemonFromPlayerParty(encounter.misc.pokemon); globalScene.removePokemonFromPlayerParty(encounter.misc.pokemon);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player a Shiny Charm // Give the player a Shiny Charm
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.SHINY_CHARM)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -152,15 +153,15 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Extort the rich kid for money // Extort the rich kid for money
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party // Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price); updatePlayerMoney(encounter.misc.price);
setEncounterExp(scene, encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true); setEncounterExp(encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -175,9 +176,9 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,23 +1,32 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig, generateModifierType, generateModifierTypeOption, generateModifierType,
generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp, leaveEncounterWithoutBattle,
setEncounterExp,
setEncounterRewards setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon } from "#app/field/pokemon";
import type {
BerryModifierType, BerryModifierType,
ModifierTypeOption } from "#app/modifier/modifier-type";
import {
getPartyLuckValue, getPartyLuckValue,
ModifierPoolType, ModifierPoolType,
ModifierTypeOption, modifierTypes, modifierTypes,
regenerateModifierPoolThresholds, regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import { randSeedInt, randSeedItem } from "#app/utils"; import { randSeedInt, randSeedItem } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -31,7 +40,8 @@ import { BerryType } from "#enums/berry-type";
import { PERMANENT_STATS, Stat } from "#enums/stat"; import { PERMANENT_STATS, Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/berriesAbound"; const namespace = "mysteryEncounters/berriesAbound";
@ -54,20 +64,20 @@ export const BerriesAboundEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
let bossSpecies: PokemonSpecies; let bossSpecies: PokemonSpecies;
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
bossSpecies = getPokemonSpecies( levelSpecies ); bossSpecies = getPokemonSpecies( levelSpecies );
} else { } else {
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
} }
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [{ pokemonConfigs: [{
@ -82,10 +92,10 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Calculate the number of extra berries that player receives // Calculate the number of extra berries that player receives
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7 // 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
const numBerries = const numBerries =
scene.currentBattle.waveIndex > 160 ? 7 globalScene.currentBattle.waveIndex > 160 ? 7
: scene.currentBattle.waveIndex > 120 ? 5 : globalScene.currentBattle.waveIndex > 120 ? 5
: scene.currentBattle.waveIndex > 40 ? 4 : 2; : globalScene.currentBattle.waveIndex > 40 ? 4 : 2;
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0); regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
encounter.misc = { numBerries }; encounter.misc = { numBerries };
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon); const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
@ -113,7 +123,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
]; ];
// Get fastest party pokemon for option 2 // Get fastest party pokemon for option 2
const fastestPokemon = getHighestStatPlayerPokemon(scene, PERMANENT_STATS[Stat.SPD], true, false); const fastestPokemon = getHighestStatPlayerPokemon(PERMANENT_STATS[Stat.SPD], true, false);
encounter.misc.fastestPokemon = fastestPokemon; encounter.misc.fastestPokemon = fastestPokemon;
encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD); encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD);
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender()); encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
@ -134,34 +144,34 @@ export const BerriesAboundEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const numBerries = encounter.misc.numBerries; const numBerries = encounter.misc.numBerries;
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
// Generate a random berry and give it to the first Pokemon with room for it // Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) { for (let i = 0; i < numBerries; i++) {
tryGiveBerry(scene); tryGiveBerry();
} }
}; };
const shopOptions: ModifierTypeOption[] = []; const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY); const mod = generateModifierTypeOption(modifierTypes.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
} }
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withOption( .withOption(
@ -171,9 +181,9 @@ export const BerriesAboundEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick race for berries // Pick race for berries
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon; const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon;
const enemySpeed: number = encounter.misc.enemySpeed; const enemySpeed: number = encounter.misc.enemySpeed;
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1); const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
@ -182,7 +192,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
const shopOptions: ModifierTypeOption[] = []; const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY); const mod = generateModifierTypeOption(modifierTypes.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
@ -193,29 +203,29 @@ export const BerriesAboundEncounter: MysteryEncounter =
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
// Generate a random berry and give it to the first Pokemon with room for it // Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) { for (let i = 0; i < numBerries; i++) {
tryGiveBerry(scene); tryGiveBerry();
} }
}; };
// Defense/Spd buffs below wave 50, +1 to all stats otherwise // Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] : [ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; const config = globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ]; config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.boss_enraged`); queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}; };
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await showEncounterText(scene, `${namespace}:option.2.selected_bad`); await showEncounterText(`${namespace}:option.2.selected_bad`);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
return; return;
} else { } else {
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2 // Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
@ -224,19 +234,19 @@ export const BerriesAboundEncounter: MysteryEncounter =
const doFasterBerryRewards = () => { const doFasterBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed }));
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first) // Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
for (let i = 0; i < numBerriesGrabbed; i++) { for (let i = 0; i < numBerriesGrabbed; i++) {
tryGiveBerry(scene, fastestPokemon); tryGiveBerry(fastestPokemon);
} }
}; };
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
await showEncounterText(scene, `${namespace}:option.2.selected`); await showEncounterText(`${namespace}:option.2.selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
}) })
.build() .build()
@ -251,38 +261,38 @@ export const BerriesAboundEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) { function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType; const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [ berryType ]) as BerryModifierType; const berry = generateModifierType(modifierTypes.BERRY, [ berryType ]) as BerryModifierType;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
// Will try to apply to prioritized pokemon first, then do normal application method if it fails // Will try to apply to prioritized pokemon first, then do normal application method if it fails
if (prioritizedPokemon) { if (prioritizedPokemon) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier const heldBerriesOfType = globalScene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier; && m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry); applyModifierTypeToPlayerPokemon(prioritizedPokemon, berry);
return; return;
} }
} }
// Iterate over the party until berry was successfully given // Iterate over the party until berry was successfully given
for (const pokemon of party) { for (const pokemon of party) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier const heldBerriesOfType = globalScene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier; && m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(scene, pokemon, berry); applyModifierTypeToPlayerPokemon(pokemon, berry);
return; return;
} }
} }

View File

@ -1,5 +1,7 @@
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig, generateModifierType, generateModifierType,
generateModifierTypeOption, generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
@ -17,17 +19,20 @@ import {
} from "#app/data/trainer-config"; } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { import {
@ -37,14 +42,17 @@ import {
TypeRequirement TypeRequirement
} from "#app/data/mystery-encounters/mystery-encounter-requirements"; } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { AttackTypeBoosterModifierType, ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import type {
PokemonHeldItemModifier
} from "#app/modifier/modifier";
import { import {
AttackTypeBoosterModifier, AttackTypeBoosterModifier,
BypassSpeedChanceModifier, BypassSpeedChanceModifier,
ContactHeldItemTransferChanceModifier, ContactHeldItemTransferChanceModifier,
GigantamaxAccessModifier, GigantamaxAccessModifier,
MegaEvolutionAccessModifier, MegaEvolutionAccessModifier
PokemonHeldItemModifier
} from "#app/modifier/modifier"; } from "#app/modifier/modifier";
import i18next from "i18next"; import i18next from "i18next";
import MoveInfoOverlay from "#app/ui/move-info-overlay"; import MoveInfoOverlay from "#app/ui/move-info-overlay";
@ -214,12 +222,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculates what trainers are available for battle in the encounter // Calculates what trainers are available for battle in the encounter
// Bug type superfan trainer config // Bug type superfan trainer config
const config = getTrainerConfigForWave(scene.currentBattle.waveIndex); const config = getTrainerConfigForWave(globalScene.currentBattle.waveIndex);
const spriteKey = config.getSpriteKey(); const spriteKey = config.getSpriteKey();
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
trainerConfig: config, trainerConfig: config,
@ -227,7 +235,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}); });
let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string }; let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string };
if (scene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false); beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false);
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false); butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false);
} else { } else {
@ -270,9 +278,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
]; ];
const requiredItems = [ const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW), generateModifierType(modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW), generateModifierType(modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]), generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]),
]; ];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/"); const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
@ -295,9 +303,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Select battle the bug trainer // Select battle the bug trainer
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
// Init the moves available for tutor // Init the moves available for tutor
@ -313,9 +321,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
// Assigns callback that teaches move before continuing to rewards // Assigns callback that teaches move before continuing to rewards
encounter.onRewards = doBugTypeMoveTutor; encounter.onRewards = doBugTypeMoveTutor;
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(true, true);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
} }
) )
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -326,17 +334,17 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip` disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Player shows off their bug types // Player shows off their bug types
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Player gets different rewards depending on the number of bug types they have // Player gets different rewards depending on the number of bug types they have
const numBugTypes = scene.getPlayerParty().filter(p => p.isOfType(Type.BUG, true)).length; const numBugTypes = globalScene.getPlayerParty().filter(p => p.isOfType(Type.BUG, true)).length;
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes }); const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
encounter.setDialogueToken("numBugTypes", numBugTypesText); encounter.setDialogueToken("numBugTypes", numBugTypesText);
if (numBugTypes < 2) { if (numBugTypes < 2) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -344,7 +352,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
]; ];
} else if (numBugTypes < 4) { } else if (numBugTypes < 4) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -352,7 +360,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
]; ];
} else if (numBugTypes < 6) { } else if (numBugTypes < 6) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -362,28 +370,28 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
} else { } else {
// If the player has any evolution/form change items that are valid for their party, // If the player has any evolution/form change items that are valid for their party,
// spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball // spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball
const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)! ]; const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(modifierTypes.MASTER_BALL)! ];
const specialOptions: ModifierTypeOption[] = []; const specialOptions: ModifierTypeOption[] = [];
if (!scene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) { if (!globalScene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.MEGA_BRACELET)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!);
} }
if (!scene.findModifier(m => m instanceof GigantamaxAccessModifier)) { if (!globalScene.findModifier(m => m instanceof GigantamaxAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.DYNAMAX_BAND)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!);
} }
const nonRareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.EVOLUTION_ITEM); const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM);
if (nonRareEvolutionModifier) { if (nonRareEvolutionModifier) {
specialOptions.push(nonRareEvolutionModifier); specialOptions.push(nonRareEvolutionModifier);
} }
const rareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.RARE_EVOLUTION_ITEM); const rareEvolutionModifier = generateModifierTypeOption(modifierTypes.RARE_EVOLUTION_ITEM);
if (rareEvolutionModifier) { if (rareEvolutionModifier) {
specialOptions.push(rareEvolutionModifier); specialOptions.push(rareEvolutionModifier);
} }
const formChangeModifier = generateModifierTypeOption(scene, modifierTypes.FORM_CHANGE_ITEM); const formChangeModifier = generateModifierTypeOption(modifierTypes.FORM_CHANGE_ITEM);
if (formChangeModifier) { if (formChangeModifier) {
specialOptions.push(formChangeModifier); specialOptions.push(formChangeModifier);
} }
const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM); const rareFormChangeModifier = generateModifierTypeOption(modifierTypes.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeModifier) { if (rareFormChangeModifier) {
specialOptions.push(rareFormChangeModifier); specialOptions.push(rareFormChangeModifier);
} }
@ -391,7 +399,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
} }
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -400,9 +408,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
]; ];
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Player shows off their bug types // Player shows off their bug types
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build()) .build())
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -429,8 +437,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
], ],
secondOptionPrompt: `${namespace}:option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
@ -466,27 +474,27 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG); (item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
}); });
if (!hasValidItem) { if (!hasValidItem) {
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null; return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier; const modifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
scene.updateModifiers(true, true); globalScene.updateModifiers(true, true);
const bugNet = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; const bugNet = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!;
bugNet.type.tier = ModifierTier.ROGUE; bugNet.type.tier = ModifierTier.ROGUE;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false });
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build()) .build())
.withOutroDialogue([ .withOutroDialogue([
@ -642,22 +650,22 @@ function getTrainerConfigForWave(waveIndex: number) {
return config; return config;
} }
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> { function doBugTypeMoveTutor(): Promise<void> {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions; const moveOptions = globalScene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
await showEncounterDialogue(scene, `${namespace}:battle_won`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:battle_won`, `${namespace}:speaker`);
const overlayScale = 1; const overlayScale = 1;
const moveInfoOverlay = new MoveInfoOverlay(scene, { const moveInfoOverlay = new MoveInfoOverlay({
delayVisibility: false, delayVisibility: false,
scale: overlayScale, scale: overlayScale,
onSide: true, onSide: true,
right: true, right: true,
x: 1, x: 1,
y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1, y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1,
width: (scene.game.canvas.width / 6) - 2, width: (globalScene.game.canvas.width / 6) - 2,
}); });
scene.ui.add(moveInfoOverlay); globalScene.ui.add(moveInfoOverlay);
const optionSelectItems = moveOptions.map((move: PokemonMove) => { const optionSelectItems = moveOptions.map((move: PokemonMove) => {
const option: OptionSelectItem = { const option: OptionSelectItem = {
@ -680,7 +688,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
moveInfoOverlay.setVisible(false); moveInfoOverlay.setVisible(false);
}; };
const result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel); const result = await selectOptionThenPokemon(optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel);
// let forceExit = !!result; // let forceExit = !!result;
if (!result) { if (!result) {
moveInfoOverlay.active = false; moveInfoOverlay.active = false;
@ -691,7 +699,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
// Option select complete, handle if they are learning a move // Option select complete, handle if they are learning a move
if (result && result.selectedOptionIndex < moveOptions.length) { if (result && result.selectedOptionIndex < moveOptions.length) {
scene.unshiftPhase(new LearnMovePhase(scene, result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId)); globalScene.unshiftPhase(new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId));
} }
// Complete battle and go to rewards // Complete battle and go to rewards

View File

@ -1,26 +1,30 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config"; import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { ModifierPoolType, modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { randSeedInt, randSeedShuffle } from "#app/utils"; import { randSeedInt, randSeedShuffle } from "#app/utils";
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import i18next from "i18next"; import i18next from "i18next";
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { Ability } from "#app/data/ability"; import { Ability } from "#app/data/ability";
import { BerryModifier } from "#app/modifier/modifier"; import { BerryModifier } from "#app/modifier/modifier";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
@ -105,8 +109,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
speaker: `${namespace}:speaker` speaker: `${namespace}:speaker`
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const clownTrainerType = TrainerType.HARLEQUIN; const clownTrainerType = TrainerType.HARLEQUIN;
const clownConfig = trainerConfigs[clownTrainerType].clone(); const clownConfig = trainerConfigs[clownTrainerType].clone();
@ -142,7 +146,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
}); });
// Load animations/sfx for start of fight moves // Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [ Moves.ROLE_PLAY, Moves.TAUNT ]); loadCustomMovesForEncounter([ Moves.ROLE_PLAY, Moves.TAUNT ]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName()); encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
@ -165,12 +169,12 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn battle // Spawn battle
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
// TODO: when Magic Room and Wonder Room are implemented, add those to start of battle // TODO: when Magic Room and Wonder Room are implemented, add those to start of battle
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
@ -193,28 +197,28 @@ export const ClowningAroundEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.withPostOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPostOptionPhase(async (): Promise<boolean> => {
// After the battle, offer the player the opportunity to permanently swap ability // After the battle, offer the player the opportunity to permanently swap ability
const abilityWasSwapped = await handleSwapAbility(scene); const abilityWasSwapped = await handleSwapAbility();
if (abilityWasSwapped) { if (abilityWasSwapped) {
await showEncounterText(scene, `${namespace}:option.1.ability_gained`); await showEncounterText(`${namespace}:option.1.ability_gained`);
} }
// Play animations once ability swap is complete // Play animations once ability swap is complete
// Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals // Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals
scene.tweens.add({ globalScene.tweens.add({
targets: scene.currentBattle.trainer, targets: globalScene.currentBattle.trainer,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
alpha: 0, alpha: 0,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 250 duration: 250
}); });
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
return true; return true;
}) })
.build() .build()
@ -239,13 +243,13 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Swap player's items on pokemon with the most items // Swap player's items on pokemon with the most items
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items // Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
// So Vitamins, form change items, etc. are not included // So Vitamins, form change items, etc. are not included
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let mostHeldItemsPokemon = party[0]; let mostHeldItemsPokemon = party[0];
let count = mostHeldItemsPokemon.getHeldItems() let count = mostHeldItemsPokemon.getHeldItems()
.filter(m => m.isTransferable && !(m instanceof BerryModifier)) .filter(m => m.isTransferable && !(m instanceof BerryModifier))
@ -270,10 +274,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
items.filter(m => m instanceof BerryModifier) items.filter(m => m instanceof BerryModifier)
.forEach(m => { .forEach(m => {
numBerries += m.stackCount; numBerries += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
}); });
generateItemsOfTier(scene, mostHeldItemsPokemon, numBerries, "Berries"); generateItemsOfTier(mostHeldItemsPokemon, numBerries, "Berries");
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm) // Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
// For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier // For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier
@ -286,24 +290,24 @@ export const ClowningAroundEncounter: MysteryEncounter =
const tier = type.tier ?? ModifierTier.ULTRA; const tier = type.tier ?? ModifierTier.ULTRA;
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) { if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
numRogue += m.stackCount; numRogue += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
} else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === ModifierTier.ULTRA) { } else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === ModifierTier.ULTRA) {
numUltra += m.stackCount; numUltra += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
} }
}); });
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA); generateItemsOfTier(mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE); generateItemsOfTier(mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); await transitionMysteryEncounterIntroVisuals(true, true, 200);
}) })
.build() .build()
) )
@ -327,10 +331,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Randomize the second type of all player's pokemon // Randomize the second type of all player's pokemon
// If the pokemon does not normally have a second type, it will gain 1 // If the pokemon does not normally have a second type, it will gain 1
for (const pokemon of scene.getPlayerParty()) { for (const pokemon of globalScene.getPlayerParty()) {
const originalTypes = pokemon.getTypes(false, false, true); const originalTypes = pokemon.getTypes(false, false, true);
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type // If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
@ -367,14 +371,14 @@ export const ClowningAroundEncounter: MysteryEncounter =
} }
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); await transitionMysteryEncounterIntroVisuals(true, true, 200);
}) })
.build() .build()
) )
@ -385,24 +389,24 @@ export const ClowningAroundEncounter: MysteryEncounter =
]) ])
.build(); .build();
async function handleSwapAbility(scene: BattleScene) { async function handleSwapAbility() {
return new Promise<boolean>(async resolve => { return new Promise<boolean>(async resolve => {
await showEncounterDialogue(scene, `${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
await showEncounterText(scene, `${namespace}:option.1.apply_ability_message`); await showEncounterText(`${namespace}:option.1.apply_ability_message`);
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve); displayYesNoOptions(resolve);
}); });
}); });
} }
function displayYesNoOptions(scene: BattleScene, resolve) { function displayYesNoOptions(resolve) {
showEncounterText(scene, `${namespace}:option.1.ability_prompt`, null, 500, false); showEncounterText(`${namespace}:option.1.ability_prompt`, null, 500, false);
const fullOptions = [ const fullOptions = [
{ {
label: i18next.t("menu:yes"), label: i18next.t("menu:yes"),
handler: () => { handler: () => {
onYesAbilitySwap(scene, resolve); onYesAbilitySwap(resolve);
return true; return true;
} }
}, },
@ -420,29 +424,29 @@ function displayYesNoOptions(scene: BattleScene, resolve) {
maxOptions: 7, maxOptions: 7,
yOffset: 0 yOffset: 0
}; };
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); globalScene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
} }
function onYesAbilitySwap(scene: BattleScene, resolve) { function onYesAbilitySwap(resolve) {
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Do ability swap // Do ability swap
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability); applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability);
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true)); globalScene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
}; };
const onPokemonNotSelected = () => { const onPokemonNotSelected = () => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve); displayYesNoOptions(resolve);
}); });
}; };
selectPokemonForOption(scene, onPokemonSelected, onPokemonNotSelected); selectPokemonForOption(onPokemonSelected, onPokemonNotSelected);
} }
function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItems: number, tier: ModifierTier | "Berries") { function generateItemsOfTier(pokemon: PlayerPokemon, numItems: number, tier: ModifierTier | "Berries") {
// These pools have to be defined at runtime so that modifierTypes exist // These pools have to be defined at runtime so that modifierTypes exist
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon // Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
// This is to prevent "over-generating" a random item of a certain type during item swaps // This is to prevent "over-generating" a random item of a certain type during item swaps
@ -495,11 +499,11 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
const newItemType = pool[randIndex]; const newItemType = pool[randIndex];
let newMod: PokemonHeldItemModifierType; let newMod: PokemonHeldItemModifierType;
if (tier === "Berries") { if (tier === "Berries") {
newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType; newMod = generateModifierType(modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
} else { } else {
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType; newMod = generateModifierType(newItemType[0]) as PokemonHeldItemModifierType;
} }
applyModifierTypeToPlayerPokemon(scene, pokemon, newMod); applyModifierTypeToPlayerPokemon(pokemon, newMod);
// Decrement max stacks and remove from pool if at max // Decrement max stacks and remove from pool if at max
newItemType[1]--; newItemType[1]--;
if (newItemType[1] <= 0) { if (newItemType[1] <= 0) {

View File

@ -1,22 +1,26 @@
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { EncounterBattleAnim } from "#app/data/battle-anims"; import { EncounterBattleAnim } from "#app/data/battle-anims";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
@ -91,10 +95,10 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withCatchAllowed(true) .withCatchAllowed(true)
.withFleeAllowed(false) .withFleeAllowed(false)
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
const oricorio = scene.getEnemyPokemon()!; const oricorio = globalScene.getEnemyPokemon()!;
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, scene.getPlayerPokemon()!); const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, globalScene.getPlayerPokemon()!);
danceAnim.play(scene, false, () => { danceAnim.play(false, () => {
if (oricorio.shiny) { if (oricorio.shiny) {
oricorio.sparkle(); oricorio.sparkle();
} }
@ -110,12 +114,12 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const species = getPokemonSpecies(Species.ORICORIO); const species = getPokemonSpecies(Species.ORICORIO);
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false); const enemyPokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, false);
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) { if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
if (enemyPokemon.moveset.length < 4) { if (enemyPokemon.moveset.length < 4) {
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE)); enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
@ -126,7 +130,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
// Set the form index based on the biome // Set the form index based on the biome
// Defaults to Baile style if somehow nothing matches // Defaults to Baile style if somehow nothing matches
const currentBiome = scene.arena.biomeType; const currentBiome = globalScene.arena.biomeType;
if (BAILE_STYLE_BIOMES.includes(currentBiome)) { if (BAILE_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 0; enemyPokemon.formIndex = 0;
} else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) { } else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) {
@ -140,14 +144,14 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
const oricorioData = new PokemonData(enemyPokemon); const oricorioData = new PokemonData(enemyPokemon);
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, false, oricorioData); const oricorio = globalScene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, false, oricorioData);
// Adds a real Pokemon sprite to the field (required for the animation) // Adds a real Pokemon sprite to the field (required for the animation)
scene.getEnemyParty().forEach(enemyPokemon => { globalScene.getEnemyParty().forEach(enemyPokemon => {
scene.field.remove(enemyPokemon, true); globalScene.field.remove(enemyPokemon, true);
}); });
scene.currentBattle.enemyParty = [ oricorio ]; globalScene.currentBattle.enemyParty = [ oricorio ];
scene.field.add(oricorio); globalScene.field.add(oricorio);
// Spawns on offscreen field // Spawns on offscreen field
oricorio.x -= 300; oricorio.x -= 300;
encounter.loadAssets.push(oricorio.loadAssets()); encounter.loadAssets.push(oricorio.loadAssets());
@ -160,8 +164,8 @@ export const DancingLessonsEncounter: MysteryEncounter =
// Gets +1 to all stats except SPD on battle start // Gets +1 to all stats except SPD on battle start
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`); queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1));
} }
}], }],
}; };
@ -186,9 +190,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -197,9 +201,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await hideOricorioPokemon(scene); await hideOricorioPokemon();
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
) )
@ -215,25 +219,25 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Learn its Dance // Learn its Dance
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
scene.unshiftPhase(new LearnMovePhase(scene, scene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE)); globalScene.unshiftPhase(new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE));
// Play animation again to "learn" the dance // Play animation again to "learn" the dance
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()); const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, globalScene.getEnemyPokemon()!, globalScene.getPlayerPokemon());
danceAnim.play(scene); danceAnim.play();
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Learn its Dance // Learn its Dance
await hideOricorioPokemon(scene); await hideOricorioPokemon();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -252,9 +256,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Open menu for selecting pokemon with a Dancing move // Open menu for selecting pokemon with a Dancing move
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for nature selection // Return the options for nature selection
return pokemon.moveset return pokemon.moveset
@ -281,20 +285,20 @@ export const DancingLessonsEncounter: MysteryEncounter =
if (!pokemon.isAllowedInBattle()) { if (!pokemon.isAllowedInBattle()) {
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null; return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
} }
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Show the Oricorio a dance, and recruit it // Show the Oricorio a dance, and recruit it
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const oricorio = encounter.misc.oricorioData.toPokemon(scene); const oricorio = encounter.misc.oricorioData.toPokemon();
oricorio.passive = true; oricorio.passive = true;
// Ensure the Oricorio's moveset gains the Dance move the player used // Ensure the Oricorio's moveset gains the Dance move the player used
@ -307,18 +311,18 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
} }
await hideOricorioPokemon(scene); await hideOricorioPokemon();
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false); await catchPokemon(oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
.build(); .build();
function hideOricorioPokemon(scene: BattleScene) { function hideOricorioPokemon() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const oricorioSprite = scene.getEnemyParty()[0]; const oricorioSprite = globalScene.getEnemyParty()[0];
scene.tweens.add({ globalScene.tweens.add({
targets: oricorioSprite, targets: oricorioSprite,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -326,7 +330,7 @@ function hideOricorioPokemon(scene: BattleScene) {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 750, duration: 750,
onComplete: () => { onComplete: () => {
scene.field.remove(oricorioSprite, true); globalScene.field.remove(oricorioSprite, true);
resolve(); resolve();
} }
}); });

View File

@ -1,18 +1,21 @@
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils"; import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
@ -138,16 +141,16 @@ export const DarkDealEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens // Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
// Will never return last battle able mon and instead pick fainted/unable to battle // Will never return last battle able mon and instead pick fainted/unable to battle
const removedPokemon = getRandomPlayerPokemon(scene, true, false, true); const removedPokemon = getRandomPlayerPokemon(true, false, true);
// Get all the pokemon's held items // Get all the pokemon's held items
const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier)); const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
scene.removePokemonFromPlayerParty(removedPokemon); globalScene.removePokemonFromPlayerParty(removedPokemon);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender()); encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
// Store removed pokemon types // Store removed pokemon types
@ -156,16 +159,16 @@ export const DarkDealEncounter: MysteryEncounter =
modifiers modifiers
}; };
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player 5 Rogue Balls // Give the player 5 Rogue Balls
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL));
// Start encounter with random legendary (7-10 starter strength) that has level additive // Start encounter with random legendary (7-10 starter strength) that has level additive
// If this is a mono-type challenge, always ensure the required type is filtered for // If this is a mono-type challenge, always ensure the required type is filtered for
let bossTypes: Type[] = encounter.misc.removedTypes; let bossTypes: Type[] = encounter.misc.removedTypes;
const singleTypeChallenges = scene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE); const singleTypeChallenges = globalScene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE);
if (scene.gameMode.isChallenge && singleTypeChallenges.length > 0) { if (globalScene.gameMode.isChallenge && singleTypeChallenges.length > 0) {
bossTypes = singleTypeChallenges.map(c => (c.value - 1) as Type); bossTypes = singleTypeChallenges.map(c => (c.value - 1) as Type);
} }
@ -191,7 +194,7 @@ export const DarkDealEncounter: MysteryEncounter =
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfig ], pokemonConfigs: [ pokemonConfig ],
}; };
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -206,9 +209,9 @@ export const DarkDealEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,18 +1,22 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PreserveBerryModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PreserveBerryModifier } from "#app/modifier/modifier";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { randSeedItem } from "#app/utils"; import { randSeedItem } from "#app/utils";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -36,19 +40,19 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
const doEventReward = (scene: BattleScene) => { const doEventReward = () => {
const event_buff = scene.eventManager.activeEvent()?.delibirdyBuff ?? []; const event_buff = globalScene.eventManager.activeEvent()?.delibirdyBuff ?? [];
if (event_buff.length > 0) { if (event_buff.length > 0) {
const candidates = event_buff.filter((c => { const candidates = event_buff.filter((c => {
const mtype = generateModifierType(scene, modifierTypes[c]); const mtype = generateModifierType(modifierTypes[c]);
const existingCharm = scene.findModifier(m => m.type.id === mtype?.id); const existingCharm = globalScene.findModifier(m => m.type.id === mtype?.id);
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount(scene)); return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
})); }));
if (candidates.length > 0) { if (candidates.length > 0) {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes[randSeedItem(candidates)])); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
} else { } else {
// At max stacks, give a Voucher instead // At max stacks, give a Voucher instead
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER));
} }
} }
}; };
@ -114,15 +118,15 @@ export const DelibirdyEncounter: MysteryEncounter =
text: `${namespace}:outro`, text: `${namespace}:outro`,
} }
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName()); encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
scene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3"); globalScene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3");
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm("mystery_encounter_delibirdy"); globalScene.fadeAndSwitchBgm("mystery_encounter_delibirdy");
return true; return true;
}) })
.withOption( .withOption(
@ -138,29 +142,29 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false); updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player an Amulet Coin // Give the player an Amulet Coin
// Check if the player has max stacks of that item already // Check if the player has max stacks of that item already
const existing = scene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier; const existing = globalScene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
doEventReward(scene); doEventReward();
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -178,8 +182,8 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -205,57 +209,57 @@ export const DelibirdyEncounter: MysteryEncounter =
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier: BerryModifier | PokemonInstantReviveModifier = encounter.misc.chosenModifier; const modifier: BerryModifier | PokemonInstantReviveModifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed // Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed
if (modifier instanceof BerryModifier) { if (modifier instanceof BerryModifier) {
// Check if the player has max stacks of that Candy Jar already // Check if the player has max stacks of that Candy Jar already
const existing = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier; const existing = globalScene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
doEventReward(scene); doEventReward();
} }
} else { } else {
// Check if the player has max stacks of that Berry Pouch already // Check if the player has max stacks of that Berry Pouch already
const existing = scene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier; const existing = globalScene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
doEventReward(scene); doEventReward();
} }
} }
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -273,8 +277,8 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -300,39 +304,39 @@ export const DelibirdyEncounter: MysteryEncounter =
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier; const modifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check if the player has max stacks of Healing Charm already // Check if the player has max stacks of Healing Charm already
const existing = scene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier; const existing = globalScene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerParty()[0], shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
doEventReward(scene); doEventReward();
} }
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )

View File

@ -2,12 +2,13 @@ import {
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
setEncounterRewards, setEncounterRewards,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type"; import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import MysteryEncounter, { import {
MysteryEncounterBuilder, MysteryEncounterBuilder,
} from "#app/data/mystery-encounters/mystery-encounter"; } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -60,7 +61,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose TMs // Choose TMs
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -77,8 +78,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -86,7 +87,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose Vitamins // Choose Vitamins
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -101,8 +102,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -110,7 +111,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose X Items // Choose X Items
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -125,8 +126,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -134,7 +135,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.4.label`, buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}:option.4.tooltip`, buttonTooltip: `${namespace}:option.4.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose Pokeballs // Choose Pokeballs
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -153,8 +154,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withOutroDialogue([ .withOutroDialogue([

View File

@ -1,12 +1,13 @@
import { MoveCategory } from "#app/data/move"; import { MoveCategory } from "#app/data/move";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
@ -64,8 +65,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -74,7 +75,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL); pokemonAndMoveChosen(pokemon, move, MoveCategory.PHYSICAL);
return true; return true;
}, },
}; };
@ -82,23 +83,23 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!, generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
@ -110,8 +111,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -120,7 +121,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL); pokemonAndMoveChosen(pokemon, move, MoveCategory.SPECIAL);
return true; return true;
}, },
}; };
@ -128,23 +129,23 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!, generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
@ -156,8 +157,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -166,7 +167,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS); pokemonAndMoveChosen(pokemon, move, MoveCategory.STATUS);
return true; return true;
}, },
}; };
@ -174,30 +175,30 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!, generateModifierTypeOption(modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!, generateModifierTypeOption(modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
.build(); .build();
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) { function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const correctMove = move.getMove().category === correctMoveCategory; const correctMove = move.getMove().category === correctMoveCategory;
encounter.setDialogueToken("pokeName", pokemon.getNameToRender()); encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
encounter.setDialogueToken("move", move.getName()); encounter.setDialogueToken("move", move.getName());
@ -214,7 +215,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
text: `${namespace}:incorrect_exp`, text: `${namespace}:incorrect_exp`,
}, },
]; ];
setEncounterExp(scene, scene.getPlayerParty().map((p) => p.id), 50); setEncounterExp(globalScene.getPlayerParty().map((p) => p.id), 50);
} else { } else {
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
@ -228,7 +229,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
text: `${namespace}:correct_exp`, text: `${namespace}:correct_exp`,
}, },
]; ];
setEncounterExp(scene, [ pokemon.id ], 100); setEncounterExp([ pokemon.id ], 100);
} }
encounter.misc = { encounter.misc = {
correctMove: correctMove, correctMove: correctMove,

View File

@ -1,16 +1,20 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type"; import { initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { modifierTypes, } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { AbilityRequirement, CombinationPokemonRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { EncounterBattleAnim } from "#app/data/battle-anims"; import { EncounterBattleAnim } from "#app/data/battle-anims";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
@ -58,8 +62,8 @@ export const FieryFalloutEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mons // Calculate boss mons
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
@ -71,7 +75,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
gender: Gender.MALE, gender: Gender.MALE,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1));
} }
}, },
{ {
@ -80,7 +84,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
gender: Gender.FEMALE, gender: Gender.FEMALE,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1));
} }
} }
], ],
@ -113,25 +117,25 @@ export const FieryFalloutEncounter: MysteryEncounter =
]; ];
// Load animations/sfx for Volcarona moves // Load animations/sfx for Volcarona moves
loadCustomMovesForEncounter(scene, [ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]); loadCustomMovesForEncounter([ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]);
scene.arena.trySetWeather(WeatherType.SUNNY, true); globalScene.arena.trySetWeather(WeatherType.SUNNY, true);
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName()); encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 200, 70, 2, 3); background.playWithoutTargets(200, 70, 2, 3);
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
animation.playWithoutTargets(scene, 80, 100, 2); animation.playWithoutTargets(80, 100, 2);
scene.time.delayedCall(600, () => { globalScene.time.delayedCall(600, () => {
animation.playWithoutTargets(scene, -20, 100, 2); animation.playWithoutTargets(-20, 100, 2);
}); });
scene.time.delayedCall(1200, () => { globalScene.time.delayedCall(1200, () => {
animation.playWithoutTargets(scene, 140, 150, 2); animation.playWithoutTargets(140, 150, 2);
}); });
return true; return true;
@ -150,10 +154,10 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem(scene)); setEncounterRewards({ fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem());
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
@ -168,7 +172,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
move: new PokemonMove(Moves.FIRE_SPIN), move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true ignorePp: true
}); });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -181,15 +185,15 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Damage non-fire types and burn 1 random non-fire type member + give it Heatproof // Damage non-fire types and burn 1 random non-fire type member + give it Heatproof
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const nonFireTypes = scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE)); const nonFireTypes = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
for (const pkm of nonFireTypes) { for (const pkm of nonFireTypes) {
const percentage = DAMAGE_PERCENTAGE / 100; const percentage = DAMAGE_PERCENTAGE / 100;
const damage = Math.floor(pkm.getMaxHp() * percentage); const damage = Math.floor(pkm.getMaxHp() * percentage);
applyDamageToPokemon(scene, pkm, damage); applyDamageToPokemon(pkm, damage);
} }
// Burn random member // Burn random member
@ -201,7 +205,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
// Burn applied // Burn applied
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender()); encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
encounter.setDialogueToken("abilityName", new Ability(Abilities.HEATPROOF, 3).name); encounter.setDialogueToken("abilityName", new Ability(Abilities.HEATPROOF, 3).name);
queueEncounterMessage(scene, `${namespace}:option.2.target_burned`); queueEncounterMessage(`${namespace}:option.2.target_burned`);
// Also permanently change the burned Pokemon's ability to Heatproof // Also permanently change the burned Pokemon's ability to Heatproof
applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF); applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF);
@ -209,7 +213,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
} }
// No rewards // No rewards
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
} }
) )
.withOption( .withOption(
@ -231,44 +235,44 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Do NOT await this, to prevent player from repeatedly pressing options // Do NOT await this, to prevent player from repeatedly pressing options
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000); transitionMysteryEncounterIntroVisuals(false, false, 2000);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Fire types help calm the Volcarona // Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
setEncounterRewards(scene, setEncounterRewards(
{ fillRemaining: true }, { fillRemaining: true },
undefined, undefined,
() => { () => {
giveLeadPokemonAttackTypeBoostItem(scene); giveLeadPokemonAttackTypeBoostItem();
}); });
const primary = encounter.options[2].primaryPokemon!; const primary = encounter.options[2].primaryPokemon!;
setEncounterExp(scene, [ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2); setEncounterExp([ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
.build(); .build();
function giveLeadPokemonAttackTypeBoostItem(scene: BattleScene) { function giveLeadPokemonAttackTypeBoostItem() {
// Give first party pokemon attack type boost item for free at end of battle // Give first party pokemon attack type boost item for free at end of battle
const leadPokemon = scene.getPlayerParty()?.[0]; const leadPokemon = globalScene.getPlayerParty()?.[0];
if (leadPokemon) { if (leadPokemon) {
// Generate type booster held item, default to Charcoal if item fails to generate // Generate type booster held item, default to Charcoal if item fails to generate
let boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType; let boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType;
if (!boosterModifierType) { if (!boosterModifierType) {
boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType; boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType;
} }
applyModifierTypeToPlayerPokemon(scene, leadPokemon, boosterModifierType); applyModifierTypeToPlayerPokemon(leadPokemon, boosterModifierType);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("itemName", boosterModifierType.name); encounter.setDialogueToken("itemName", boosterModifierType.name);
encounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender()); encounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}:found_item`); queueEncounterMessage(`${namespace}:found_item`);
} }
} }

View File

@ -1,23 +1,28 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp, leaveEncounterWithoutBattle,
setEncounterExp,
setEncounterRewards setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon } from "#app/field/pokemon";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import type {
ModifierTypeOption } from "#app/modifier/modifier-type";
import { import {
getPartyLuckValue, getPartyLuckValue,
getPlayerModifierTypeOptions, getPlayerModifierTypeOptions,
ModifierPoolType, ModifierPoolType,
ModifierTypeOption,
regenerateModifierPoolThresholds, regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -29,7 +34,8 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
import { randSeedInt, randSeedItem } from "#app/utils"; import { randSeedInt, randSeedItem } from "#app/utils";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/fightOrFlight"; const namespace = "mysteryEncounters/fightOrFlight";
@ -52,20 +58,20 @@ export const FightOrFlightEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
let bossSpecies: PokemonSpecies; let bossSpecies: PokemonSpecies;
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
bossSpecies = getPokemonSpecies( levelSpecies ); bossSpecies = getPokemonSpecies( levelSpecies );
} else { } else {
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
} }
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender()); encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [{ pokemonConfigs: [{
@ -75,10 +81,10 @@ export const FightOrFlightEncounter: MysteryEncounter =
isBoss: true, isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`); queueEncounterMessage(`${namespace}:option.1.stat_boost`);
// Randomly boost 1 stat 2 stages // Randomly boost 1 stat 2 stages
// Cannot boost Spd, Acc, or Evasion // Cannot boost Spd, Acc, or Evasion
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2));
} }
}], }],
}; };
@ -87,18 +93,18 @@ export const FightOrFlightEncounter: MysteryEncounter =
// Calculate item // Calculate item
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER // Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
const tier = const tier =
scene.currentBattle.waveIndex > 160 globalScene.currentBattle.waveIndex > 160
? ModifierTier.MASTER ? ModifierTier.MASTER
: scene.currentBattle.waveIndex > 120 : globalScene.currentBattle.waveIndex > 120
? ModifierTier.ROGUE ? ModifierTier.ROGUE
: scene.currentBattle.waveIndex > 40 : globalScene.currentBattle.waveIndex > 40
? ModifierTier.ULTRA ? ModifierTier.ULTRA
: ModifierTier.GREAT; : ModifierTier.GREAT;
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0); regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
let item: ModifierTypeOption | null = null; let item: ModifierTypeOption | null = null;
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward // TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") { while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
item = getPlayerModifierTypeOptions(1, scene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0]; item = getPlayerModifierTypeOptions(1, globalScene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
} }
encounter.setDialogueToken("itemName", item.type.name); encounter.setDialogueToken("itemName", item.type.name);
encounter.misc = item; encounter.misc = item;
@ -144,12 +150,12 @@ export const FightOrFlightEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
// Pokemon will randomly boost 1 stat by 2 stages // Pokemon will randomly boost 1 stat by 2 stages
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withOption( .withOption(
@ -166,16 +172,16 @@ export const FightOrFlightEncounter: MysteryEncounter =
} }
] ]
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick steal // Pick steal
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
// Use primaryPokemon to execute the thievery // Use primaryPokemon to execute the thievery
const primaryPokemon = encounter.options[1].primaryPokemon!; const primaryPokemon = encounter.options[1].primaryPokemon!;
setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -189,9 +195,9 @@ export const FightOrFlightEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,10 +1,13 @@
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { FieldPosition } from "#app/field/pokemon";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -80,14 +83,14 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3"); globalScene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName()); encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm("mystery_encounter_fun_and_games"); globalScene.fadeAndSwitchBgm("mystery_encounter_fun_and_games");
return true; return true;
}) })
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -102,9 +105,9 @@ export const FunAndGamesEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Select Pokemon for minigame // Select Pokemon for minigame
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.misc = { encounter.misc = {
playerPokemon: pokemon, playerPokemon: pokemon,
@ -113,28 +116,28 @@ export const FunAndGamesEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be selected // Only Pokemon that are not KOed/legal can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Start minigame // Start minigame
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.misc.turnsRemaining = 3; encounter.misc.turnsRemaining = 3;
// Update money // Update money
const moneyCost = (encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney; const moneyCost = (encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney;
updatePlayerMoney(scene, -moneyCost, true, false); updatePlayerMoney(-moneyCost, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost })); await showEncounterText(i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost }));
// Handlers for battle events // Handlers for battle events
encounter.onTurnStart = handleNextTurn; // triggered during TurnInitPhase encounter.onTurnStart = handleNextTurn; // triggered during TurnInitPhase
encounter.doContinueEncounter = handleLoseMinigame; // triggered during MysteryEncounterRewardsPhase, post VictoryPhase if the player KOs Wobbuffet encounter.doContinueEncounter = handleLoseMinigame; // triggered during MysteryEncounterRewardsPhase, post VictoryPhase if the player KOs Wobbuffet
hideShowmanIntroSprite(scene); hideShowmanIntroSprite();
await summonPlayerPokemon(scene); await summonPlayerPokemon();
await showWobbuffetHealthBar(scene); await showWobbuffetHealthBar();
return true; return true;
}) })
@ -150,22 +153,22 @@ export const FunAndGamesEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
await transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(true, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
async function summonPlayerPokemon(scene: BattleScene) { async function summonPlayerPokemon() {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const playerPokemon = encounter.misc.playerPokemon; const playerPokemon = encounter.misc.playerPokemon;
// Swaps the chosen Pokemon and the first player's lead Pokemon in the party // Swaps the chosen Pokemon and the first player's lead Pokemon in the party
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
const chosenIndex = party.indexOf(playerPokemon); const chosenIndex = party.indexOf(playerPokemon);
if (chosenIndex !== 0) { if (chosenIndex !== 0) {
const leadPokemon = party[0]; const leadPokemon = party[0];
@ -175,36 +178,36 @@ async function summonPlayerPokemon(scene: BattleScene) {
// Do trainer summon animation // Do trainer summon animation
let playerAnimationPromise: Promise<void> | undefined; let playerAnimationPromise: Promise<void> | undefined;
scene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) })); globalScene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) }));
scene.pbTray.hide(); globalScene.pbTray.hide();
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
scene.time.delayedCall(562, () => { globalScene.time.delayedCall(562, () => {
scene.trainer.setFrame("2"); globalScene.trainer.setFrame("2");
scene.time.delayedCall(64, () => { globalScene.time.delayedCall(64, () => {
scene.trainer.setFrame("3"); globalScene.trainer.setFrame("3");
}); });
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: scene.trainer, targets: globalScene.trainer,
x: -36, x: -36,
duration: 1000, duration: 1000,
onComplete: () => scene.trainer.setVisible(false) onComplete: () => globalScene.trainer.setVisible(false)
}); });
scene.time.delayedCall(750, () => { globalScene.time.delayedCall(750, () => {
playerAnimationPromise = summonPlayerPokemonAnimation(scene, playerPokemon); playerAnimationPromise = summonPlayerPokemonAnimation(playerPokemon);
}); });
// Also loads Wobbuffet data (cannot be shiny) // Also loads Wobbuffet data (cannot be shiny)
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET); const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true); const wobbuffet = globalScene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true);
wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ]; wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ];
wobbuffet.setNature(Nature.MILD); wobbuffet.setNature(Nature.MILD);
wobbuffet.setAlpha(0); wobbuffet.setAlpha(0);
wobbuffet.setVisible(false); wobbuffet.setVisible(false);
wobbuffet.calculateStats(); wobbuffet.calculateStats();
scene.currentBattle.enemyParty[0] = wobbuffet; globalScene.currentBattle.enemyParty[0] = wobbuffet;
scene.gameData.setPokemonSeen(wobbuffet, true); globalScene.gameData.setPokemonSeen(wobbuffet, true);
await wobbuffet.loadAssets(); await wobbuffet.loadAssets();
const id = setInterval(checkPlayerAnimationPromise, 500); const id = setInterval(checkPlayerAnimationPromise, 500);
async function checkPlayerAnimationPromise() { async function checkPlayerAnimationPromise() {
@ -217,37 +220,37 @@ async function summonPlayerPokemon(scene: BattleScene) {
}); });
} }
function handleLoseMinigame(scene: BattleScene) { function handleLoseMinigame() {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
// Check Wobbuffet is still alive // Check Wobbuffet is still alive
const wobbuffet = scene.getEnemyPokemon(); const wobbuffet = globalScene.getEnemyPokemon();
if (!wobbuffet || wobbuffet.isFainted(true) || wobbuffet.hp === 0) { if (!wobbuffet || wobbuffet.isFainted(true) || wobbuffet.hp === 0) {
// Player loses // Player loses
// End the battle // End the battle
if (wobbuffet) { if (wobbuffet) {
wobbuffet.hideInfo(); wobbuffet.hideInfo();
scene.field.remove(wobbuffet); globalScene.field.remove(wobbuffet);
} }
transitionMysteryEncounterIntroVisuals(scene, true, true); transitionMysteryEncounterIntroVisuals(true, true);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
await showEncounterText(scene, `${namespace}:ko`); await showEncounterText(`${namespace}:ko`);
const reviveCost = scene.getWaveMoneyAmount(1.5); const reviveCost = globalScene.getWaveMoneyAmount(1.5);
updatePlayerMoney(scene, -reviveCost, true, false); updatePlayerMoney(-reviveCost, true, false);
} }
resolve(); resolve();
}); });
} }
function handleNextTurn(scene: BattleScene) { function handleNextTurn() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const wobbuffet = scene.getEnemyPokemon(); const wobbuffet = globalScene.getEnemyPokemon();
if (!wobbuffet) { if (!wobbuffet) {
// Should never be triggered, just handling the edge case // Should never be triggered, just handling the edge case
handleLoseMinigame(scene); handleLoseMinigame();
return true; return true;
} }
if (encounter.misc.turnsRemaining <= 0) { if (encounter.misc.turnsRemaining <= 0) {
@ -257,15 +260,15 @@ function handleNextTurn(scene: BattleScene) {
let isHealPhase = false; let isHealPhase = false;
if (healthRatio < 0.03) { if (healthRatio < 0.03) {
// Grand prize // Grand prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:best_result`; resultMessageKey = `${namespace}:best_result`;
} else if (healthRatio < 0.15) { } else if (healthRatio < 0.15) {
// 2nd prize // 2nd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:great_result`; resultMessageKey = `${namespace}:great_result`;
} else if (healthRatio < 0.33) { } else if (healthRatio < 0.33) {
// 3rd prize // 3rd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:good_result`; resultMessageKey = `${namespace}:good_result`;
} else { } else {
// No prize // No prize
@ -275,22 +278,22 @@ function handleNextTurn(scene: BattleScene) {
// End the battle // End the battle
wobbuffet.hideInfo(); wobbuffet.hideInfo();
scene.field.remove(wobbuffet); globalScene.field.remove(wobbuffet);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, isHealPhase); leaveEncounterWithoutBattle(isHealPhase);
// Must end the TurnInit phase prematurely so battle phases aren't added to queue // Must end the TurnInit phase prematurely so battle phases aren't added to queue
queueEncounterMessage(scene, `${namespace}:end_game`); queueEncounterMessage(`${namespace}:end_game`);
queueEncounterMessage(scene, resultMessageKey); queueEncounterMessage(resultMessageKey);
// Skip remainder of TurnInitPhase // Skip remainder of TurnInitPhase
return true; return true;
} else { } else {
if (encounter.misc.turnsRemaining < 3) { if (encounter.misc.turnsRemaining < 3) {
// Display charging messages on turns that aren't the initial turn // Display charging messages on turns that aren't the initial turn
queueEncounterMessage(scene, `${namespace}:charging_continue`); queueEncounterMessage(`${namespace}:charging_continue`);
} }
queueEncounterMessage(scene, `${namespace}:turn_remaining_${encounter.misc.turnsRemaining}`); queueEncounterMessage(`${namespace}:turn_remaining_${encounter.misc.turnsRemaining}`);
encounter.misc.turnsRemaining--; encounter.misc.turnsRemaining--;
} }
@ -298,33 +301,33 @@ function handleNextTurn(scene: BattleScene) {
return false; return false;
} }
async function showWobbuffetHealthBar(scene: BattleScene) { async function showWobbuffetHealthBar() {
const wobbuffet = scene.getEnemyPokemon()!; const wobbuffet = globalScene.getEnemyPokemon()!;
scene.add.existing(wobbuffet); globalScene.add.existing(wobbuffet);
scene.field.add(wobbuffet); globalScene.field.add(wobbuffet);
const playerPokemon = scene.getPlayerPokemon() as Pokemon; const playerPokemon = globalScene.getPlayerPokemon() as Pokemon;
if (playerPokemon?.isOnField()) { if (playerPokemon?.isOnField()) {
scene.field.moveBelow(wobbuffet, playerPokemon); globalScene.field.moveBelow(wobbuffet, playerPokemon);
} }
// Show health bar and trigger cry // Show health bar and trigger cry
wobbuffet.showInfo(); wobbuffet.showInfo();
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
wobbuffet.cry(); wobbuffet.cry();
}); });
wobbuffet.resetSummonData(); wobbuffet.resetSummonData();
// Track the HP change across turns // Track the HP change across turns
scene.currentBattle.mysteryEncounter!.misc.wobbuffetHealth = wobbuffet.hp; globalScene.currentBattle.mysteryEncounter!.misc.wobbuffetHealth = wobbuffet.hp;
} }
function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon): Promise<void> { function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const pokeball = scene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball)); const pokeball = globalScene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball));
pokeball.setVisible(false); pokeball.setVisible(false);
pokeball.setOrigin(0.5, 0.625); pokeball.setOrigin(0.5, 0.625);
scene.field.add(pokeball); globalScene.field.add(pokeball);
pokemon.setFieldPosition(FieldPosition.CENTER, 0); pokemon.setFieldPosition(FieldPosition.CENTER, 0);
@ -332,32 +335,32 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokeball.setVisible(true); pokeball.setVisible(true);
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 650, duration: 650,
x: 100 + fpOffset[0] x: 100 + fpOffset[0]
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 150, duration: 150,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
y: 70 + fpOffset[1], y: 70 + fpOffset[1],
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 500, duration: 500,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
angle: 1440, angle: 1440,
y: 132 + fpOffset[1], y: 132 + fpOffset[1],
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokeball.destroy(); pokeball.destroy();
scene.add.existing(pokemon); globalScene.add.existing(pokemon);
scene.field.add(pokemon); globalScene.field.add(pokemon);
addPokeballOpenParticles(scene, pokemon.x, pokemon.y - 16, pokemon.pokeball); addPokeballOpenParticles(pokemon.x, pokemon.y - 16, pokemon.pokeball);
scene.updateModifiers(true); globalScene.updateModifiers(true);
scene.updateFieldScale(); globalScene.updateFieldScale();
pokemon.showInfo(); pokemon.showInfo();
pokemon.playAnim(); pokemon.playAnim();
pokemon.setVisible(true); pokemon.setVisible(true);
@ -365,8 +368,8 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokemon.setScale(0.5); pokemon.setScale(0.5);
pokemon.tint(getPokeballTintColor(pokemon.pokeball)); pokemon.tint(getPokeballTintColor(pokemon.pokeball));
pokemon.untint(250, "Sine.easeIn"); pokemon.untint(250, "Sine.easeIn");
scene.updateFieldScale(); globalScene.updateFieldScale();
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 250, duration: 250,
ease: "Sine.easeIn", ease: "Sine.easeIn",
@ -375,15 +378,15 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
pokemon.getSprite().clearTint(); pokemon.getSprite().clearTint();
pokemon.resetSummonData(); pokemon.resetSummonData();
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
if (pokemon.isShiny()) { if (pokemon.isShiny()) {
scene.unshiftPhase(new ShinySparklePhase(scene, pokemon.getBattlerIndex())); globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
} }
pokemon.resetTurnData(); pokemon.resetTurnData();
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
scene.pushPhase(new PostSummonPhase(scene, pokemon.getBattlerIndex())); globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
resolve(); resolve();
}); });
} }
@ -395,13 +398,13 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
}); });
} }
function hideShowmanIntroSprite(scene: BattleScene) { function hideShowmanIntroSprite() {
const carnivalGame = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0]; const carnivalGame = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0];
const wobbuffet = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0]; const wobbuffet = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0];
const showMan = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0]; const showMan = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0];
// Hide the showman // Hide the showman
scene.tweens.add({ globalScene.tweens.add({
targets: showMan, targets: showMan,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -411,7 +414,7 @@ function hideShowmanIntroSprite(scene: BattleScene) {
}); });
// Slide the Wobbuffet and Game over slightly // Slide the Wobbuffet and Game over slightly
scene.tweens.add({ globalScene.tweens.add({
targets: [ wobbuffet, carnivalGame ], targets: [ wobbuffet, carnivalGame ],
x: "+=16", x: "+=16",
ease: "Sine.easeInOut", ease: "Sine.easeInOut",

View File

@ -2,20 +2,26 @@ import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterReward
import { TrainerSlot, } from "#app/data/trainer-config"; import { TrainerSlot, } from "#app/data/trainer-config";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { MusicPreference } from "#app/system/settings/settings"; import { MusicPreference } from "#app/system/settings/settings";
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import type { ModifierTypeOption } from "#app/modifier/modifier-type";
import { getPlayerModifierTypeOptions, ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils"; import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import type Pokemon from "#app/field/pokemon";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import i18next from "i18next"; import i18next from "i18next";
import { Gender, getGenderSymbol } from "#app/data/gender"; import { Gender, getGenderSymbol } from "#app/data/gender";
@ -102,24 +108,24 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Load bgm // Load bgm
let bgmKey: string; let bgmKey: string;
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
bgmKey = "mystery_encounter_gen_5_gts"; bgmKey = "mystery_encounter_gen_5_gts";
scene.loadBgm(bgmKey, `${bgmKey}.mp3`); globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
} else { } else {
// Mixed option // Mixed option
bgmKey = "mystery_encounter_gen_6_gts"; bgmKey = "mystery_encounter_gen_6_gts";
scene.loadBgm(bgmKey, `${bgmKey}.mp3`); globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
} }
// Load possible trade options // Load possible trade options
// Maps current party member's id to 3 EnemyPokemon objects // Maps current party member's id to 3 EnemyPokemon objects
// None of the trade options can be the same species // None of the trade options can be the same species
const tradeOptionsMap: Map<number, EnemyPokemon[]> = getPokemonTradeOptions(scene); const tradeOptionsMap: Map<number, EnemyPokemon[]> = getPokemonTradeOptions();
encounter.misc = { encounter.misc = {
tradeOptionsMap, tradeOptionsMap,
bgmKey bgmKey
@ -127,8 +133,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey); globalScene.fadeAndSwitchBgm(globalScene.currentBattle.mysteryEncounter!.misc.bgmKey);
return true; return true;
}) })
.withOption( .withOption(
@ -140,8 +146,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get the trade species options for the selected pokemon // Get the trade species options for the selected pokemon
const tradeOptionsMap: Map<number, EnemyPokemon[]> = encounter.misc.tradeOptionsMap; const tradeOptionsMap: Map<number, EnemyPokemon[]> = encounter.misc.tradeOptionsMap;
@ -165,17 +171,17 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null; const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null;
const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : ""); const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : "");
const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : ""); const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false); showEncounterText(`${line1}\n${line2}`, 0, 0, false);
}, },
}; };
return option; return option;
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon; const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon; const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier)); const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier));
@ -185,32 +191,32 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
// Remove the original party member from party // Remove the original party member from party
scene.removePokemonFromPlayerParty(tradedPokemon, false); globalScene.removePokemonFromPlayerParty(tradedPokemon, false);
// Set data properly, then generate the new Pokemon's assets // Set data properly, then generate the new Pokemon's assets
receivedPokemonData.passive = tradedPokemon.passive; receivedPokemonData.passive = tradedPokemon.passive;
// Pokeball to Ultra ball, randomly // Pokeball to Ultra ball, randomly
receivedPokemonData.pokeball = randInt(4) as PokeballType; receivedPokemonData.pokeball = randInt(4) as PokeballType;
const dataSource = new PokemonData(receivedPokemonData); const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource); const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getPlayerParty().push(newPlayerPokemon); globalScene.getPlayerParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets(); await newPlayerPokemon.loadAssets();
for (const mod of modifiers) { for (const mod of modifiers) {
mod.pokemonId = newPlayerPokemon.id; mod.pokemonId = newPlayerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground();
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000); await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); globalScene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground();
tradedPokemon.destroy(); tradedPokemon.destroy();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -222,19 +228,19 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Randomly generate a Wonder Trade pokemon // Randomly generate a Wonder Trade pokemon
const randomTradeOption = generateTradeOption(scene.getPlayerParty().map(p => p.species)); const randomTradeOption = generateTradeOption(globalScene.getPlayerParty().map(p => p.species));
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); const tradePokemon = new EnemyPokemon(randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms) // Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) { if (!tradePokemon.shiny) {
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE); const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
if (scene.eventManager.isEventActive()) { if (globalScene.eventManager.isEventActive()) {
shinyThreshold.value *= scene.eventManager.getShinyMultiplier(); shinyThreshold.value *= globalScene.eventManager.getShinyMultiplier();
} }
scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
// Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms // Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms
// Maximum shiny chance of 4096/65536 -> 1/16, cannot improve further after that // Maximum shiny chance of 4096/65536 -> 1/16, cannot improve further after that
@ -248,7 +254,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
if (tradePokemon.species.abilityHidden) { if (tradePokemon.species.abilityHidden) {
if (tradePokemon.abilityIndex < hiddenIndex) { if (tradePokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new NumberHolder(64); const hiddenAbilityChance = new NumberHolder(64);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
@ -281,10 +287,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.misc.receivedPokemon = tradePokemon; encounter.misc.receivedPokemon = tradePokemon;
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon; const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon; const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier)); const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier));
@ -294,31 +300,31 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
// Remove the original party member from party // Remove the original party member from party
scene.removePokemonFromPlayerParty(tradedPokemon, false); globalScene.removePokemonFromPlayerParty(tradedPokemon, false);
// Set data properly, then generate the new Pokemon's assets // Set data properly, then generate the new Pokemon's assets
receivedPokemonData.passive = tradedPokemon.passive; receivedPokemonData.passive = tradedPokemon.passive;
receivedPokemonData.pokeball = randInt(4) as PokeballType; receivedPokemonData.pokeball = randInt(4) as PokeballType;
const dataSource = new PokemonData(receivedPokemonData); const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource); const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getPlayerParty().push(newPlayerPokemon); globalScene.getPlayerParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets(); await newPlayerPokemon.loadAssets();
for (const mod of modifiers) { for (const mod of modifiers) {
mod.pokemonId = newPlayerPokemon.id; mod.pokemonId = newPlayerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground();
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000); await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); globalScene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground();
tradedPokemon.destroy(); tradedPokemon.destroy();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -330,8 +336,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -359,18 +365,18 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return it.isTransferable; return it.isTransferable;
}).length > 0; }).length > 0;
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null; return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier as PokemonHeldItemModifier; const modifier = encounter.misc.chosenModifier as PokemonHeldItemModifier;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check tier of the traded item, the received item will be one tier up // Check tier of the traded item, the received item will be one tier up
@ -397,16 +403,16 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
} }
encounter.setDialogueToken("itemName", item.type.name); encounter.setDialogueToken("itemName", item.type.name);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
await scene.updateModifiers(true, true); await globalScene.updateModifiers(true, true);
// Generate a trainer name // Generate a trainer name
const traderName = generateRandomTraderName(); const traderName = generateRandomTraderName();
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
await showEncounterText(scene, `${namespace}:item_trade_selected`); await showEncounterText(`${namespace}:item_trade_selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -420,26 +426,26 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> { function getPokemonTradeOptions(): Map<number, EnemyPokemon[]> {
const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>(); const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>();
// Starts by filtering out any current party members as valid resulting species // Starts by filtering out any current party members as valid resulting species
const alreadyUsedSpecies: PokemonSpecies[] = scene.getPlayerParty().map(p => p.species); const alreadyUsedSpecies: PokemonSpecies[] = globalScene.getPlayerParty().map(p => p.species);
scene.getPlayerParty().forEach(pokemon => { globalScene.getPlayerParty().forEach(pokemon => {
// If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools // If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) { if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
const generation = pokemon.species.generation; const generation = pokemon.species.generation;
const tradeOptions: EnemyPokemon[] = LEGENDARY_TRADE_POOLS[generation].map(s => { const tradeOptions: EnemyPokemon[] = LEGENDARY_TRADE_POOLS[generation].map(s => {
const pokemonSpecies = getPokemonSpecies(s); const pokemonSpecies = getPokemonSpecies(s);
return new EnemyPokemon(scene, pokemonSpecies, 5, TrainerSlot.NONE, false); return new EnemyPokemon(pokemonSpecies, 5, TrainerSlot.NONE, false);
}); });
tradeOptionsMap.set(pokemon.id, tradeOptions); tradeOptionsMap.set(pokemon.id, tradeOptions);
} else { } else {
@ -454,7 +460,7 @@ function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]>
// Add trade options to map // Add trade options to map
tradeOptionsMap.set(pokemon.id, tradeOptions.map(s => { tradeOptionsMap.set(pokemon.id, tradeOptions.map(s => {
return new EnemyPokemon(scene, s, pokemon.level, TrainerSlot.NONE, false); return new EnemyPokemon(s, pokemon.level, TrainerSlot.NONE, false);
})); }));
} }
}); });
@ -497,28 +503,28 @@ function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?:
return newSpecies!; return newSpecies!;
} }
function showTradeBackground(scene: BattleScene) { function showTradeBackground() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.add.container(0, -scene.game.canvas.height / 6); const tradeContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6);
tradeContainer.setName("Trade Background"); tradeContainer.setName("Trade Background");
const flyByStaticBg = scene.add.rectangle(0, 0, scene.game.canvas.width / 6, scene.game.canvas.height / 6, 0); const flyByStaticBg = globalScene.add.rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0);
flyByStaticBg.setName("Black Background"); flyByStaticBg.setName("Black Background");
flyByStaticBg.setOrigin(0, 0); flyByStaticBg.setOrigin(0, 0);
flyByStaticBg.setVisible(false); flyByStaticBg.setVisible(false);
tradeContainer.add(flyByStaticBg); tradeContainer.add(flyByStaticBg);
const tradeBaseBg = scene.add.image(0, 0, "default_bg"); const tradeBaseBg = globalScene.add.image(0, 0, "default_bg");
tradeBaseBg.setName("Trade Background Image"); tradeBaseBg.setName("Trade Background Image");
tradeBaseBg.setOrigin(0, 0); tradeBaseBg.setOrigin(0, 0);
tradeContainer.add(tradeBaseBg); tradeContainer.add(tradeBaseBg);
scene.fieldUI.add(tradeContainer); globalScene.fieldUI.add(tradeContainer);
scene.fieldUI.bringToTop(tradeContainer); globalScene.fieldUI.bringToTop(tradeContainer);
tradeContainer.setVisible(true); tradeContainer.setVisible(true);
tradeContainer.alpha = 0; tradeContainer.alpha = 0;
scene.tweens.add({ globalScene.tweens.add({
targets: tradeContainer, targets: tradeContainer,
alpha: 1, alpha: 1,
duration: 500, duration: 500,
@ -530,17 +536,17 @@ function showTradeBackground(scene: BattleScene) {
}); });
} }
function hideTradeBackground(scene: BattleScene) { function hideTradeBackground() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const transformationContainer = scene.fieldUI.getByName("Trade Background"); const transformationContainer = globalScene.fieldUI.getByName("Trade Background");
scene.tweens.add({ globalScene.tweens.add({
targets: transformationContainer, targets: transformationContainer,
alpha: 0, alpha: 0,
duration: 1000, duration: 1000,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
onComplete: () => { onComplete: () => {
scene.fieldUI.remove(transformationContainer, true); globalScene.fieldUI.remove(transformationContainer, true);
resolve(); resolve();
} }
}); });
@ -549,13 +555,12 @@ function hideTradeBackground(scene: BattleScene) {
/** /**
* Initiates an "evolution-like" animation to transform a previousPokemon (presumably from the player's party) into a new one, not necessarily an evolution species. * Initiates an "evolution-like" animation to transform a previousPokemon (presumably from the player's party) into a new one, not necessarily an evolution species.
* @param scene
* @param tradedPokemon * @param tradedPokemon
* @param receivedPokemon * @param receivedPokemon
*/ */
function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) { function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
let tradedPokemonSprite: Phaser.GameObjects.Sprite; let tradedPokemonSprite: Phaser.GameObjects.Sprite;
@ -564,8 +569,8 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
let receivedPokemonTintSprite: Phaser.GameObjects.Sprite; let receivedPokemonTintSprite: Phaser.GameObjects.Sprite;
const getPokemonSprite = () => { const getPokemonSprite = () => {
const ret = scene.addPokemonSprite(tradedPokemon, tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pkmn__sub"); const ret = globalScene.addPokemonSprite(tradedPokemon, tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pkmn__sub");
ret.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret; return ret;
}; };
@ -589,7 +594,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", tradedPokemon.shiny); sprite.setPipelineData("shiny", tradedPokemon.shiny);
@ -610,7 +615,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", receivedPokemon.shiny); sprite.setPipelineData("shiny", receivedPokemon.shiny);
@ -625,45 +630,45 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
// Traded pokemon pokeball // Traded pokemon pokeball
const tradedPbAtlasKey = getPokeballAtlasKey(tradedPokemon.pokeball); const tradedPbAtlasKey = getPokeballAtlasKey(tradedPokemon.pokeball);
const tradedPokeball: Phaser.GameObjects.Sprite = scene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", tradedPbAtlasKey); const tradedPokeball: Phaser.GameObjects.Sprite = globalScene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", tradedPbAtlasKey);
tradedPokeball.setVisible(false); tradedPokeball.setVisible(false);
tradeContainer.add(tradedPokeball); tradeContainer.add(tradedPokeball);
// Received pokemon pokeball // Received pokemon pokeball
const receivedPbAtlasKey = getPokeballAtlasKey(receivedPokemon.pokeball); const receivedPbAtlasKey = getPokeballAtlasKey(receivedPokemon.pokeball);
const receivedPokeball: Phaser.GameObjects.Sprite = scene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", receivedPbAtlasKey); const receivedPokeball: Phaser.GameObjects.Sprite = globalScene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", receivedPbAtlasKey);
receivedPokeball.setVisible(false); receivedPokeball.setVisible(false);
tradeContainer.add(receivedPokeball); tradeContainer.add(receivedPokeball);
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 500, duration: 500,
onComplete: async () => { onComplete: async () => {
scene.fadeOutBgm(1000, false); globalScene.fadeOutBgm(1000, false);
await showEncounterText(scene, `${namespace}:pokemon_trade_selected`); await showEncounterText(`${namespace}:pokemon_trade_selected`);
tradedPokemon.cry(); tradedPokemon.cry();
scene.playBgm("evolution"); globalScene.playBgm("evolution");
await showEncounterText(scene, `${namespace}:pokemon_trade_goodbye`); await showEncounterText(`${namespace}:pokemon_trade_goodbye`);
tradedPokeball.setAlpha(0); tradedPokeball.setAlpha(0);
tradedPokeball.setVisible(true); tradedPokeball.setVisible(true);
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 250, duration: 250,
onComplete: () => { onComplete: () => {
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`); tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`)); globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`));
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
tradedPokemonTintSprite.setVisible(true); tradedPokemonTintSprite.setVisible(true);
// TODO: need to add particles to fieldUI instead of field // TODO: need to add particles to fieldUI instead of field
// addPokeballOpenParticles(scene, tradedPokemon.x, tradedPokemon.y, tradedPokemon.pokeball); // addPokeballOpenParticles(tradedPokemon.x, tradedPokemon.y, tradedPokemon.pokeball);
scene.tweens.add({ globalScene.tweens.add({
targets: [ tradedPokemonTintSprite, tradedPokemonSprite ], targets: [ tradedPokemonTintSprite, tradedPokemonSprite ],
duration: 500, duration: 500,
ease: "Sine.easeIn", ease: "Sine.easeIn",
@ -672,30 +677,30 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
tradedPokemonSprite.setVisible(false); tradedPokemonSprite.setVisible(false);
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`); tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
tradedPokemonTintSprite.setVisible(false); tradedPokemonTintSprite.setVisible(false);
scene.playSound("se/pb_catch"); globalScene.playSound("se/pb_catch");
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`)); globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`));
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
y: "+=10", y: "+=10",
duration: 200, duration: 200,
delay: 250, delay: 250,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_bounce_1"); globalScene.playSound("se/pb_bounce_1");
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
y: "-=100", y: "-=100",
duration: 200, duration: 200,
delay: 1000, delay: 1000,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
onStart: () => { onStart: () => {
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
}, },
onComplete: async () => { onComplete: async () => {
await doPokemonTradeFlyBySequence(scene, tradedPokemonSprite, receivedPokemonSprite); await doPokemonTradeFlyBySequence(tradedPokemonSprite, receivedPokemonSprite);
await doTradeReceivedSequence(scene, receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey); await doTradeReceivedSequence(receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey);
resolve(); resolve();
} }
}); });
@ -710,9 +715,9 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
}); });
} }
function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonSprite: Phaser.GameObjects.Sprite) { function doPokemonTradeFlyBySequence(tradedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonSprite: Phaser.GameObjects.Sprite) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
const flyByStaticBg = tradeContainer.getByName("Black Background") as Phaser.GameObjects.Rectangle; const flyByStaticBg = tradeContainer.getByName("Black Background") as Phaser.GameObjects.Rectangle;
flyByStaticBg.setVisible(true); flyByStaticBg.setVisible(true);
@ -733,47 +738,47 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
const BASE_ANIM_DURATION = 1000; const BASE_ANIM_DURATION = 1000;
// Fade out trade background // Fade out trade background
scene.tweens.add({ globalScene.tweens.add({
targets: tradeBaseBg, targets: tradeBaseBg,
alpha: 0, alpha: 0,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: FADE_DELAY, duration: FADE_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: [ receivedPokemonSprite, tradedPokemonSprite ], targets: [ receivedPokemonSprite, tradedPokemonSprite ],
y: tradeBaseBg.displayWidth / 2 - 100, y: tradeBaseBg.displayWidth / 2 - 100,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 3, duration: BASE_ANIM_DURATION * 3,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
x: tradeBaseBg.displayWidth / 4, x: tradeBaseBg.displayWidth / 4,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION / 2, duration: BASE_ANIM_DURATION / 2,
delay: ANIM_DELAY delay: ANIM_DELAY
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
x: tradeBaseBg.displayWidth * 3 / 4, x: tradeBaseBg.displayWidth * 3 / 4,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION / 2, duration: BASE_ANIM_DURATION / 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
y: "+=200", y: "+=200",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 2, duration: BASE_ANIM_DURATION * 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
y: "-=200", y: "-=200",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 2, duration: BASE_ANIM_DURATION * 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: tradeBaseBg, targets: tradeBaseBg,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
@ -793,9 +798,9 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
}); });
} }
function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPokemon, receivedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonTintSprite: Phaser.GameObjects.Sprite, receivedPokeballSprite: Phaser.GameObjects.Sprite, receivedPbAtlasKey: string) { function doTradeReceivedSequence(receivedPokemon: PlayerPokemon, receivedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonTintSprite: Phaser.GameObjects.Sprite, receivedPokeballSprite: Phaser.GameObjects.Sprite, receivedPbAtlasKey: string) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
receivedPokemonSprite.setVisible(false); receivedPokemonSprite.setVisible(false);
@ -812,7 +817,7 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
// Received pokemon sparkles // Received pokemon sparkles
let pokemonShinySparkle: Phaser.GameObjects.Sprite; let pokemonShinySparkle: Phaser.GameObjects.Sprite;
if (receivedPokemon.shiny) { if (receivedPokemon.shiny) {
pokemonShinySparkle = scene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny"); pokemonShinySparkle = globalScene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny");
pokemonShinySparkle.setVisible(false); pokemonShinySparkle.setVisible(false);
tradeContainer.add(pokemonShinySparkle); tradeContainer.add(pokemonShinySparkle);
} }
@ -820,19 +825,19 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
const BASE_ANIM_DURATION = 1000; const BASE_ANIM_DURATION = 1000;
// Pokeball falls to the screen // Pokeball falls to the screen
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokeballSprite, targets: receivedPokeballSprite,
y: "+=100", y: "+=100",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION, duration: BASE_ANIM_DURATION,
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_bounce_1"); globalScene.playSound("se/pb_bounce_1");
scene.time.delayedCall(100, () => scene.playSound("se/pb_bounce_1")); globalScene.time.delayedCall(100, () => globalScene.playSound("se/pb_bounce_1"));
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
scene.fadeOutBgm(500, false); globalScene.fadeOutBgm(500, false);
receivedPokemon.cry(); receivedPokemon.cry();
receivedPokemonTintSprite.scale = 0.25; receivedPokemonTintSprite.scale = 0.25;
receivedPokemonTintSprite.alpha = 1; receivedPokemonTintSprite.alpha = 1;
@ -841,14 +846,14 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
receivedPokemonTintSprite.alpha = 1; receivedPokemonTintSprite.alpha = 1;
receivedPokemonTintSprite.setVisible(true); receivedPokemonTintSprite.setVisible(true);
receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_opening`); receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_opening`);
scene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`)); globalScene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`));
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
scale: 1 scale: 1
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonTintSprite, targets: receivedPokemonTintSprite,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
@ -856,12 +861,12 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
alpha: 0, alpha: 0,
onComplete: () => { onComplete: () => {
if (receivedPokemon.shiny) { if (receivedPokemon.shiny) {
scene.time.delayedCall(500, () => { globalScene.time.delayedCall(500, () => {
doShinySparkleAnim(scene, pokemonShinySparkle, receivedPokemon.variant); doShinySparkleAnim(pokemonShinySparkle, receivedPokemon.variant);
}); });
} }
receivedPokeballSprite.destroy(); receivedPokeballSprite.destroy();
scene.time.delayedCall(2000, () => resolve()); globalScene.time.delayedCall(2000, () => resolve());
} }
}); });
}); });

View File

@ -2,8 +2,9 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species"; import { Species } from "#app/enums/species";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils";
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -41,8 +42,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
]) ])
.withIntroDialogue([{ text: `${namespace}:intro` }]) .withIntroDialogue([{ text: `${namespace}:intro` }])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName()); encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName());
@ -70,7 +71,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .withOptionPhase(async () => handlePokemonGuidingYouPhase())
.build() .build()
) )
.withOption( .withOption(
@ -89,7 +90,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .withOptionPhase(async () => handlePokemonGuidingYouPhase())
.build() .build()
) )
.withSimpleOption( .withSimpleOption(
@ -103,16 +104,16 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const allowedPokemon = scene.getPlayerParty().filter((p) => p.isAllowedInBattle()); const allowedPokemon = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle());
for (const pkm of allowedPokemon) { for (const pkm of allowedPokemon) {
const percentage = DAMAGE_PERCENTAGE / 100; const percentage = DAMAGE_PERCENTAGE / 100;
const damage = Math.floor(pkm.getMaxHp() * percentage); const damage = Math.floor(pkm.getMaxHp() * percentage);
applyDamageToPokemon(scene, pkm, damage); applyDamageToPokemon(pkm, damage);
} }
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
return true; return true;
} }
@ -126,19 +127,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
/** /**
* Generic handler for using a guiding pokemon to guide you back. * Generic handler for using a guiding pokemon to guide you back.
*
* @param scene Battle scene
*/ */
function handlePokemonGuidingYouPhase(scene: BattleScene) { function handlePokemonGuidingYouPhase() {
const laprasSpecies = getPokemonSpecies(Species.LAPRAS); const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
const { mysteryEncounter } = scene.currentBattle; const { mysteryEncounter } = globalScene.currentBattle;
if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) { if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) {
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true); setEncounterExp(mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
} else { } else {
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?"); console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
} }
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
return true; return true;
} }

View File

@ -1,5 +1,6 @@
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
setEncounterRewards, setEncounterRewards,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
@ -13,9 +14,10 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -37,12 +39,12 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculates what trainers are available for battle in the encounter // Calculates what trainers are available for battle in the encounter
// Normal difficulty trainer is randomly pulled from biome // Normal difficulty trainer is randomly pulled from biome
const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); const normalTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
const normalConfig = trainerConfigs[normalTrainerType].clone(); const normalConfig = trainerConfigs[normalTrainerType].clone();
let female = false; let female = false;
if (normalConfig.hasGenders) { if (normalConfig.hasGenders) {
@ -57,16 +59,16 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
// Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100 // Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100
let retries = 0; let retries = 0;
let hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); let hardTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
while (retries < 5 && hardTrainerType === normalTrainerType) { while (retries < 5 && hardTrainerType === normalTrainerType) {
// Will try to use a different trainer from the normal trainer type // Will try to use a different trainer from the normal trainer type
hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); hardTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
retries++; retries++;
} }
const hardTemplate = new TrainerPartyCompoundTemplate( const hardTemplate = new TrainerPartyCompoundTemplate(
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
new TrainerPartyTemplate( new TrainerPartyTemplate(
Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5), Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 20), 5),
PartyMemberStrength.AVERAGE, PartyMemberStrength.AVERAGE,
false, false,
true true
@ -87,8 +89,8 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome // Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons // They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
const brutalTrainerType = scene.arena.randomTrainerType( const brutalTrainerType = globalScene.arena.randomTrainerType(
scene.currentBattle.waveIndex, globalScene.currentBattle.waveIndex,
true true
); );
const e4Template = trainerPartyTemplates.ELITE_FOUR; const e4Template = trainerPartyTemplates.ELITE_FOUR;
@ -145,18 +147,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn standard trainer battle with memory mushroom reward // Spawn standard trainer battle with memory mushroom reward
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 10); }, globalScene.currentBattle.waveIndex * 10);
await initBattlePromise!; await initBattlePromise!;
} }
) )
@ -170,18 +172,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn hard fight // Spawn hard fight
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 100); }, globalScene.currentBattle.waveIndex * 100);
await initBattlePromise!; await initBattlePromise!;
} }
) )
@ -195,21 +197,21 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn brutal fight // Spawn brutal fight
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
// To avoid player level snowballing from picking this option // To avoid player level snowballing from picking this option
encounter.expMultiplier = 0.9; encounter.expMultiplier = 0.9;
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 1000); }, globalScene.currentBattle.waveIndex * 1000);
await initBattlePromise!; await initBattlePromise!;
} }
) )

View File

@ -1,8 +1,10 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -66,8 +68,8 @@ export const MysteriousChestEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
@ -106,9 +108,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Play animation // Play animation
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const introVisuals = encounter.introVisuals!; const introVisuals = encounter.introVisuals!;
// Determine roll first // Determine roll first
@ -128,13 +130,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
introVisuals.spriteConfigs[1].disableAnimation = false; introVisuals.spriteConfigs[1].disableAnimation = false;
introVisuals.playAnim(); introVisuals.playAnim();
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Open the chest // Open the chest
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const roll = encounter.misc.roll; const roll = encounter.misc.roll;
if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) { if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) {
// Choose between 2 COMMON / 2 GREAT tier items (20%) // Choose between 2 COMMON / 2 GREAT tier items (20%)
setEncounterRewards(scene, { setEncounterRewards({
guaranteedModifierTiers: [ guaranteedModifierTiers: [
ModifierTier.COMMON, ModifierTier.COMMON,
ModifierTier.COMMON, ModifierTier.COMMON,
@ -143,11 +145,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.normal`); queueEncounterMessage(`${namespace}:option.1.normal`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%) // Choose between 3 ULTRA tier items (30%)
setEncounterRewards(scene, { setEncounterRewards({
guaranteedModifierTiers: [ guaranteedModifierTiers: [
ModifierTier.ULTRA, ModifierTier.ULTRA,
ModifierTier.ULTRA, ModifierTier.ULTRA,
@ -155,39 +157,39 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.good`); queueEncounterMessage(`${namespace}:option.1.good`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// Choose between 2 ROGUE tier items (10%) // Choose between 2 ROGUE tier items (10%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]}); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.great`); queueEncounterMessage(`${namespace}:option.1.great`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
// Choose 1 MASTER tier item (5%) // Choose 1 MASTER tier item (5%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]}); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.MASTER ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.amazing`); queueEncounterMessage(`${namespace}:option.1.amazing`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else { } else {
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%) // Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true, false); const highestLevelPokemon = getHighestLevelPlayerPokemon(true, false);
koPlayerPokemon(scene, highestLevelPokemon); koPlayerPokemon(highestLevelPokemon);
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender()); encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
await showEncounterText(scene, `${namespace}:option.1.bad`); await showEncounterText(`${namespace}:option.1.bad`);
// Handle game over edge case // Handle game over edge case
const allowedPokemon = scene.getPokemonAllowedInBattle(); const allowedPokemon = globalScene.getPokemonAllowedInBattle();
if (allowedPokemon.length === 0) { if (allowedPokemon.length === 0) {
// If there are no longer any legal pokemon in the party, game over. // If there are no longer any legal pokemon in the party, game over.
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.unshiftPhase(new GameOverPhase(scene)); globalScene.unshiftPhase(new GameOverPhase());
} else { } else {
// Show which Pokemon was KOed, then start battle against Gimmighoul // Show which Pokemon was KOed, then start battle against Gimmighoul
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
} }
} }
}) })
@ -203,9 +205,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,8 +1,9 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -10,7 +11,8 @@ import { Stat } from "#enums/stat";
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import i18next from "i18next"; import i18next from "i18next";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -52,20 +54,20 @@ export const PartTimerEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
// Load sfx // Load sfx
scene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav"); globalScene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav");
scene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav"); globalScene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav");
scene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav"); globalScene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav");
scene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav"); globalScene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav");
scene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav"); globalScene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav");
scene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav"); globalScene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav");
scene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav"); globalScene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav");
scene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav"); globalScene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav");
scene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav"); globalScene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav");
scene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav"); globalScene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav");
return true; return true;
}) })
@ -84,8 +86,8 @@ export const PartTimerEncounter: MysteryEncounter =
} }
] ]
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
@ -109,41 +111,41 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, pokemon.id, 100); setEncounterExp(pokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doDeliverySfx(scene); doDeliverySfx();
}; };
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick Deliveries // Pick Deliveries
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
const moneyMultiplier = scene.currentBattle.mysteryEncounter!.misc.moneyMultiplier; const moneyMultiplier = globalScene.currentBattle.mysteryEncounter!.misc.moneyMultiplier;
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -158,8 +160,8 @@ export const PartTimerEncounter: MysteryEncounter =
} }
] ]
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
@ -186,41 +188,41 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, pokemon.id, 100); setEncounterExp(pokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doStrongWorkSfx(scene); doStrongWorkSfx();
}; };
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick Move Warehouse items // Pick Move Warehouse items
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
const moneyMultiplier = scene.currentBattle.mysteryEncounter!.misc.moneyMultiplier; const moneyMultiplier = globalScene.currentBattle.mysteryEncounter!.misc.moneyMultiplier;
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -238,8 +240,8 @@ export const PartTimerEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const selectedPokemon = encounter.selectedOption?.primaryPokemon!; const selectedPokemon = encounter.selectedOption?.primaryPokemon!;
encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender());
@ -251,28 +253,28 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, selectedPokemon.id, 100); setEncounterExp(selectedPokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doSalesSfx(scene); doSalesSfx();
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Assist with Sales // Assist with Sales
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
// Give money and do dialogue // Give money and do dialogue
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
const moneyChange = scene.getWaveMoneyAmount(2.5); const moneyChange = globalScene.getWaveMoneyAmount(2.5);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -284,51 +286,51 @@ export const PartTimerEncounter: MysteryEncounter =
]) ])
.build(); .build();
function doStrongWorkSfx(scene: BattleScene) { function doStrongWorkSfx() {
scene.playSound("battle_anims/PRSFX- Horn Drill1"); globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
scene.playSound("battle_anims/PRSFX- Horn Drill1"); globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
scene.playSound("battle_anims/PRSFX- Guillotine2"); globalScene.playSound("battle_anims/PRSFX- Guillotine2");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Heavy Slam2"); globalScene.playSound("battle_anims/PRSFX- Heavy Slam2");
}); });
scene.time.delayedCall(2500, () => { globalScene.time.delayedCall(2500, () => {
scene.playSound("battle_anims/PRSFX- Guillotine2"); globalScene.playSound("battle_anims/PRSFX- Guillotine2");
}); });
} }
function doDeliverySfx(scene: BattleScene) { function doDeliverySfx() {
scene.playSound("battle_anims/PRSFX- Accelerock1"); globalScene.playSound("battle_anims/PRSFX- Accelerock1");
scene.time.delayedCall(1500, () => { globalScene.time.delayedCall(1500, () => {
scene.playSound("battle_anims/PRSFX- Extremespeed1"); globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Extremespeed1"); globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
}); });
scene.time.delayedCall(2250, () => { globalScene.time.delayedCall(2250, () => {
scene.playSound("battle_anims/PRSFX- Agility"); globalScene.playSound("battle_anims/PRSFX- Agility");
}); });
} }
function doSalesSfx(scene: BattleScene) { function doSalesSfx() {
scene.playSound("battle_anims/PRSFX- Captivate"); globalScene.playSound("battle_anims/PRSFX- Captivate");
scene.time.delayedCall(1500, () => { globalScene.time.delayedCall(1500, () => {
scene.playSound("battle_anims/PRSFX- Attract2"); globalScene.playSound("battle_anims/PRSFX- Attract2");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Aurora Veil2"); globalScene.playSound("battle_anims/PRSFX- Aurora Veil2");
}); });
scene.time.delayedCall(3000, () => { globalScene.time.delayedCall(3000, () => {
scene.playSound("battle_anims/PRSFX- Attract2"); globalScene.playSound("battle_anims/PRSFX- Attract2");
}); });
} }

View File

@ -1,15 +1,18 @@
import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier"; import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
import { EnemyPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { IntegerHolder, randSeedInt } from "#app/utils"; import { IntegerHolder, randSeedInt } from "#app/utils";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterCost, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterCost, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -59,8 +62,8 @@ export const SafariZoneEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString()); globalScene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
return true; return true;
}) })
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -75,25 +78,25 @@ export const SafariZoneEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Start safari encounter // Start safari encounter
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.continuousEncounter = true; encounter.continuousEncounter = true;
encounter.misc = { encounter.misc = {
safariPokemonRemaining: NUM_SAFARI_ENCOUNTERS safariPokemonRemaining: NUM_SAFARI_ENCOUNTERS
}; };
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
// Load bait/mud assets // Load bait/mud assets
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav"); globalScene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims", "PRSFX- Sludge Bomb2.wav"); globalScene.loadSe("PRSFX- Sludge Bomb2", "battle_anims", "PRSFX- Sludge Bomb2.wav");
scene.loadSe("PRSFX- Taunt2", "battle_anims", "PRSFX- Taunt2.wav"); globalScene.loadSe("PRSFX- Taunt2", "battle_anims", "PRSFX- Taunt2.wav");
scene.loadAtlas("safari_zone_bait", "mystery-encounters"); globalScene.loadAtlas("safari_zone_bait", "mystery-encounters");
scene.loadAtlas("safari_zone_mud", "mystery-encounters"); globalScene.loadAtlas("safari_zone_mud", "mystery-encounters");
// Clear enemy party // Clear enemy party
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
await summonSafariPokemon(scene); await summonSafariPokemon();
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, hideDescription: true }); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, hideDescription: true });
return true; return true;
}) })
.build() .build()
@ -108,9 +111,9 @@ export const SafariZoneEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
@ -143,26 +146,26 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
} }
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Throw a ball option // Throw a ball option
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = encounter.misc.pokemon; const pokemon = encounter.misc.pokemon;
const catchResult = await throwPokeball(scene, pokemon); const catchResult = await throwPokeball(pokemon);
if (catchResult) { if (catchResult) {
// You caught pokemon // You caught pokemon
// Check how many safari pokemon left // Check how many safari pokemon left
if (encounter.misc.safariPokemonRemaining > 0) { if (encounter.misc.safariPokemonRemaining > 0) {
await summonSafariPokemon(scene); await summonSafariPokemon();
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 0, hideDescription: true }); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: 0, hideDescription: true });
} else { } else {
// End safari mode // End safari mode
encounter.continuousEncounter = false; encounter.continuousEncounter = false;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
} }
} else { } else {
// Pokemon catch failed, end turn // Pokemon catch failed, end turn
await doEndTurn(scene, 0); await doEndTurn(0);
} }
return true; return true;
}) })
@ -178,22 +181,22 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Throw bait option // Throw bait option
const pokemon = scene.currentBattle.mysteryEncounter!.misc.pokemon; const pokemon = globalScene.currentBattle.mysteryEncounter!.misc.pokemon;
await throwBait(scene, pokemon); await throwBait(pokemon);
// 100% chance to increase catch stage +2 // 100% chance to increase catch stage +2
tryChangeCatchStage(scene, 2); tryChangeCatchStage(2);
// 80% chance to increase flee stage +1 // 80% chance to increase flee stage +1
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8); const fleeChangeResult = tryChangeFleeStage(1, 8);
if (!fleeChangeResult) { if (!fleeChangeResult) {
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.busy_eating`) ?? "", null, 1000, false ); await showEncounterText(getEncounterText(`${namespace}:safari.busy_eating`) ?? "", null, 1000, false );
} else { } else {
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.eating`) ?? "", null, 1000, false); await showEncounterText(getEncounterText(`${namespace}:safari.eating`) ?? "", null, 1000, false);
} }
await doEndTurn(scene, 1); await doEndTurn(1);
return true; return true;
}) })
.build(), .build(),
@ -208,21 +211,21 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Throw mud option // Throw mud option
const pokemon = scene.currentBattle.mysteryEncounter!.misc.pokemon; const pokemon = globalScene.currentBattle.mysteryEncounter!.misc.pokemon;
await throwMud(scene, pokemon); await throwMud(pokemon);
// 100% chance to decrease flee stage -2 // 100% chance to decrease flee stage -2
tryChangeFleeStage(scene, -2); tryChangeFleeStage(-2);
// 80% chance to decrease catch stage -1 // 80% chance to decrease catch stage -1
const catchChangeResult = tryChangeCatchStage(scene, -1, 8); const catchChangeResult = tryChangeCatchStage(-1, 8);
if (!catchChangeResult) { if (!catchChangeResult) {
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.beside_itself_angry`) ?? "", null, 1000, false ); await showEncounterText(getEncounterText(`${namespace}:safari.beside_itself_angry`) ?? "", null, 1000, false );
} else { } else {
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.angry`) ?? "", null, 1000, false ); await showEncounterText(getEncounterText(`${namespace}:safari.angry`) ?? "", null, 1000, false );
} }
await doEndTurn(scene, 2); await doEndTurn(2);
return true; return true;
}) })
.build(), .build(),
@ -232,40 +235,40 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
buttonLabel: `${namespace}:safari.4.label`, buttonLabel: `${namespace}:safari.4.label`,
buttonTooltip: `${namespace}:safari.4.tooltip`, buttonTooltip: `${namespace}:safari.4.tooltip`,
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Flee option // Flee option
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = encounter.misc.pokemon; const pokemon = encounter.misc.pokemon;
await doPlayerFlee(scene, pokemon); await doPlayerFlee(pokemon);
// Check how many safari pokemon left // Check how many safari pokemon left
if (encounter.misc.safariPokemonRemaining > 0) { if (encounter.misc.safariPokemonRemaining > 0) {
await summonSafariPokemon(scene); await summonSafariPokemon();
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 3, hideDescription: true }); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: 3, hideDescription: true });
} else { } else {
// End safari mode // End safari mode
encounter.continuousEncounter = false; encounter.continuousEncounter = false;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
} }
return true; return true;
}) })
.build() .build()
]; ];
async function summonSafariPokemon(scene: BattleScene) { async function summonSafariPokemon() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Message pokemon remaining // Message pokemon remaining
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining); encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
scene.queueMessage(getEncounterText(scene, `${namespace}:safari.remaining_count`) ?? "", null, true); globalScene.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken // Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal // Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
let enemySpecies; let enemySpecies;
let pokemon; let pokemon;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
enemySpecies = getSafariSpeciesSpawn(); enemySpecies = getSafariSpeciesSpawn();
const level = scene.currentBattle.getLevelForWave(); const level = globalScene.currentBattle.getLevelForWave();
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode)); enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, globalScene.gameMode));
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false); pokemon = globalScene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
// Roll shiny twice // Roll shiny twice
if (!pokemon.shiny) { if (!pokemon.shiny) {
@ -277,7 +280,7 @@ async function summonSafariPokemon(scene: BattleScene) {
const hiddenIndex = pokemon.species.ability2 ? 2 : 1; const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
if (pokemon.abilityIndex < hiddenIndex) { if (pokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256); const hiddenAbilityChance = new IntegerHolder(256);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
@ -289,10 +292,10 @@ async function summonSafariPokemon(scene: BattleScene) {
pokemon.calculateStats(); pokemon.calculateStats();
scene.currentBattle.enemyParty.unshift(pokemon); globalScene.currentBattle.enemyParty.unshift(pokemon);
}, scene.currentBattle.waveIndex * 1000 * encounter.misc.safariPokemonRemaining); }, globalScene.currentBattle.waveIndex * 1000 * encounter.misc.safariPokemonRemaining);
scene.gameData.setPokemonSeen(pokemon, true); globalScene.gameData.setPokemonSeen(pokemon, true);
await pokemon.loadAssets(); await pokemon.loadAssets();
// Reset safari catch and flee rates // Reset safari catch and flee rates
@ -301,7 +304,7 @@ async function summonSafariPokemon(scene: BattleScene) {
encounter.misc.pokemon = pokemon; encounter.misc.pokemon = pokemon;
encounter.misc.safariPokemonRemaining -= 1; encounter.misc.safariPokemonRemaining -= 1;
scene.unshiftPhase(new SummonPhase(scene, 0, false)); globalScene.unshiftPhase(new SummonPhase(0, false));
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
@ -310,49 +313,49 @@ async function summonSafariPokemon(scene: BattleScene) {
// shows up and the IV scanner breaks. For now, we place the IV scanner code // shows up and the IV scanner breaks. For now, we place the IV scanner code
// separately so that at least the IV scanner works. // separately so that at least the IV scanner works.
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier); const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
if (ivScannerModifier) { if (ivScannerModifier) {
scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6))); globalScene.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
} }
} }
function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> { function throwPokeball(pokemon: EnemyPokemon): Promise<boolean> {
const baseCatchRate = pokemon.species.catchRate; const baseCatchRate = pokemon.species.catchRate;
// Catch stage ranges from -6 to +6 (like stat boost stages) // Catch stage ranges from -6 to +6 (like stat boost stages)
const safariCatchStage = scene.currentBattle.mysteryEncounter!.misc.catchStage; const safariCatchStage = globalScene.currentBattle.mysteryEncounter!.misc.catchStage;
// Catch modifier ranges from 2/8 (-6 stage) to 8/2 (+6) // Catch modifier ranges from 2/8 (-6 stage) to 8/2 (+6)
const safariModifier = (2 + Math.min(Math.max(safariCatchStage, 0), 6)) / (2 - Math.max(Math.min(safariCatchStage, 0), -6)); const safariModifier = (2 + Math.min(Math.max(safariCatchStage, 0), 6)) / (2 - Math.max(Math.min(safariCatchStage, 0), -6));
// Catch rate same as safari ball // Catch rate same as safari ball
const pokeballMultiplier = 1.5; const pokeballMultiplier = 1.5;
const catchRate = Math.round(baseCatchRate * pokeballMultiplier * safariModifier); const catchRate = Math.round(baseCatchRate * pokeballMultiplier * safariModifier);
const ballTwitchRate = Math.round(1048560 / Math.sqrt(Math.sqrt(16711680 / catchRate))); const ballTwitchRate = Math.round(1048560 / Math.sqrt(Math.sqrt(16711680 / catchRate)));
return trainerThrowPokeball(scene, pokemon, PokeballType.POKEBALL, ballTwitchRate); return trainerThrowPokeball(pokemon, PokeballType.POKEBALL, ballTwitchRate);
} }
async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> { async function throwBait(pokemon: EnemyPokemon): Promise<boolean> {
const originalY: number = pokemon.y; const originalY: number = pokemon.y;
const fpOffset = pokemon.getFieldPositionOffset(); const fpOffset = pokemon.getFieldPositionOffset();
const bait: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 25, "safari_zone_bait", "0001.png"); const bait: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(16 + 75, 80 + 25, "safari_zone_bait", "0001.png");
bait.setOrigin(0.5, 0.625); bait.setOrigin(0.5, 0.625);
scene.field.add(bait); globalScene.field.add(bait);
return new Promise(resolve => { return new Promise(resolve => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
// Trainer throw frames // Trainer throw frames
scene.trainer.setFrame("2"); globalScene.trainer.setFrame("2");
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
scene.trainer.setFrame("3"); globalScene.trainer.setFrame("3");
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
}); });
}); });
// Pokeball move and catch logic // Pokeball move and catch logic
scene.tweens.add({ globalScene.tweens.add({
targets: bait, targets: bait,
x: { value: 210 + fpOffset[0], ease: "Linear" }, x: { value: 210 + fpOffset[0], ease: "Linear" },
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" }, y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
@ -360,8 +363,8 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
onComplete: () => { onComplete: () => {
let index = 1; let index = 1;
scene.time.delayedCall(768, () => { globalScene.time.delayedCall(768, () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 150, duration: 150,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
@ -369,12 +372,12 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
y: originalY - 5, y: originalY - 5,
loop: 6, loop: 6,
onStart: () => { onStart: () => {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
bait.setFrame("0002.png"); bait.setFrame("0002.png");
}, },
onLoop: () => { onLoop: () => {
if (index % 2 === 0) { if (index % 2 === 0) {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
} }
if (index === 4) { if (index === 4) {
bait.setFrame("0003.png"); bait.setFrame("0003.png");
@ -382,7 +385,7 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
index++; index++;
}, },
onComplete: () => { onComplete: () => {
scene.time.delayedCall(256, () => { globalScene.time.delayedCall(256, () => {
bait.destroy(); bait.destroy();
resolve(true); resolve(true);
}); });
@ -395,55 +398,55 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
}); });
} }
async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> { async function throwMud(pokemon: EnemyPokemon): Promise<boolean> {
const originalY: number = pokemon.y; const originalY: number = pokemon.y;
const fpOffset = pokemon.getFieldPositionOffset(); const fpOffset = pokemon.getFieldPositionOffset();
const mud: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 35, "safari_zone_mud", "0001.png"); const mud: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(16 + 75, 80 + 35, "safari_zone_mud", "0001.png");
mud.setOrigin(0.5, 0.625); mud.setOrigin(0.5, 0.625);
scene.field.add(mud); globalScene.field.add(mud);
return new Promise(resolve => { return new Promise(resolve => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
// Trainer throw frames // Trainer throw frames
scene.trainer.setFrame("2"); globalScene.trainer.setFrame("2");
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
scene.trainer.setFrame("3"); globalScene.trainer.setFrame("3");
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => { globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
}); });
}); });
// Mud throw and splat // Mud throw and splat
scene.tweens.add({ globalScene.tweens.add({
targets: mud, targets: mud,
x: { value: 230 + fpOffset[0], ease: "Linear" }, x: { value: 230 + fpOffset[0], ease: "Linear" },
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" }, y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
duration: 500, duration: 500,
onComplete: () => { onComplete: () => {
// Mud frame 2 // Mud frame 2
scene.playSound("battle_anims/PRSFX- Sludge Bomb2"); globalScene.playSound("battle_anims/PRSFX- Sludge Bomb2");
mud.setFrame("0002.png"); mud.setFrame("0002.png");
// Mud splat // Mud splat
scene.time.delayedCall(200, () => { globalScene.time.delayedCall(200, () => {
mud.setFrame("0003.png"); mud.setFrame("0003.png");
scene.time.delayedCall(400, () => { globalScene.time.delayedCall(400, () => {
mud.setFrame("0004.png"); mud.setFrame("0004.png");
}); });
}); });
// Fade mud then angry animation // Fade mud then angry animation
scene.tweens.add({ globalScene.tweens.add({
targets: mud, targets: mud,
alpha: 0, alpha: 0,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
duration: 1000, duration: 1000,
onComplete: () => { onComplete: () => {
mud.destroy(); mud.destroy();
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 300, duration: 300,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
@ -451,10 +454,10 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<bool
y: originalY - 20, y: originalY - 20,
loop: 1, loop: 1,
onStart: () => { onStart: () => {
scene.playSound("battle_anims/PRSFX- Taunt2"); globalScene.playSound("battle_anims/PRSFX- Taunt2");
}, },
onLoop: () => { onLoop: () => {
scene.playSound("battle_anims/PRSFX- Taunt2"); globalScene.playSound("battle_anims/PRSFX- Taunt2");
}, },
onComplete: () => { onComplete: () => {
resolve(true); resolve(true);
@ -478,53 +481,53 @@ function isPokemonFlee(pokemon: EnemyPokemon, fleeStage: number): boolean {
return roll < fleeRate; return roll < fleeRate;
} }
function tryChangeFleeStage(scene: BattleScene, change: number, chance?: number): boolean { function tryChangeFleeStage(change: number, chance?: number): boolean {
if (chance && randSeedInt(10) >= chance) { if (chance && randSeedInt(10) >= chance) {
return false; return false;
} }
const currentFleeStage = scene.currentBattle.mysteryEncounter!.misc.fleeStage ?? 0; const currentFleeStage = globalScene.currentBattle.mysteryEncounter!.misc.fleeStage ?? 0;
scene.currentBattle.mysteryEncounter!.misc.fleeStage = Math.min(Math.max(currentFleeStage + change, -6), 6); globalScene.currentBattle.mysteryEncounter!.misc.fleeStage = Math.min(Math.max(currentFleeStage + change, -6), 6);
return true; return true;
} }
function tryChangeCatchStage(scene: BattleScene, change: number, chance?: number): boolean { function tryChangeCatchStage(change: number, chance?: number): boolean {
if (chance && randSeedInt(10) >= chance) { if (chance && randSeedInt(10) >= chance) {
return false; return false;
} }
const currentCatchStage = scene.currentBattle.mysteryEncounter!.misc.catchStage ?? 0; const currentCatchStage = globalScene.currentBattle.mysteryEncounter!.misc.catchStage ?? 0;
scene.currentBattle.mysteryEncounter!.misc.catchStage = Math.min(Math.max(currentCatchStage + change, -6), 6); globalScene.currentBattle.mysteryEncounter!.misc.catchStage = Math.min(Math.max(currentCatchStage + change, -6), 6);
return true; return true;
} }
async function doEndTurn(scene: BattleScene, cursorIndex: number) { async function doEndTurn(cursorIndex: number) {
// First cleanup and destroy old Pokemon objects that were left in the enemyParty // First cleanup and destroy old Pokemon objects that were left in the enemyParty
// They are left in enemyParty temporarily so that VictoryPhase properly handles EXP // They are left in enemyParty temporarily so that VictoryPhase properly handles EXP
const party = scene.getEnemyParty(); const party = globalScene.getEnemyParty();
if (party.length > 1) { if (party.length > 1) {
for (let i = 1; i < party.length; i++) { for (let i = 1; i < party.length; i++) {
party[i].destroy(); party[i].destroy();
} }
scene.currentBattle.enemyParty = party.slice(0, 1); globalScene.currentBattle.enemyParty = party.slice(0, 1);
} }
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = encounter.misc.pokemon; const pokemon = encounter.misc.pokemon;
const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage); const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage);
if (isFlee) { if (isFlee) {
// Pokemon flees! // Pokemon flees!
await doPokemonFlee(scene, pokemon); await doPokemonFlee(pokemon);
// Check how many safari pokemon left // Check how many safari pokemon left
if (encounter.misc.safariPokemonRemaining > 0) { if (encounter.misc.safariPokemonRemaining > 0) {
await summonSafariPokemon(scene); await summonSafariPokemon();
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
} else { } else {
// End safari mode // End safari mode
encounter.continuousEncounter = false; encounter.continuousEncounter = false;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
} }
} else { } else {
scene.queueMessage(getEncounterText(scene, `${namespace}:safari.watching`) ?? "", 0, null, 1000); globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
} }
} }

View File

@ -1,18 +1,20 @@
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon, isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon, isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
import { getNatureName } from "#app/data/nature"; import { getNatureName } from "#app/data/nature";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import i18next from "i18next"; import i18next from "i18next";
@ -79,15 +81,15 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Update money // Update money
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
// Calculate modifiers and dialogue tokens // Calculate modifiers and dialogue tokens
const modifiers = [ const modifiers = [
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!, generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!, generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
]; ];
encounter.setDialogueToken("boost1", modifiers[0].name); encounter.setDialogueToken("boost1", modifiers[0].name);
encounter.setDialogueToken("boost2", modifiers[1].name); encounter.setDialogueToken("boost2", modifiers[1].name);
@ -103,34 +105,34 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
if (!pokemon.isAllowedInChallenge()) { if (!pokemon.isAllowedInChallenge()) {
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null; return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
} }
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) { if (!encounter.pokemonMeetsPrimaryRequirements(pokemon)) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Choose Cheap Option // Choose Cheap Option
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
const modifiers = encounter.misc.modifiers; const modifiers = encounter.misc.modifiers;
for (const modType of modifiers) { for (const modType of modifiers) {
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); await applyModifierTypeToPlayerPokemon(chosenPokemon, modType);
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Damage and status applied after dealer leaves (to make thematic sense) // Damage and status applied after dealer leaves (to make thematic sense)
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon as PlayerPokemon; const chosenPokemon = encounter.misc.chosenPokemon as PlayerPokemon;
// Pokemon takes half max HP damage and nature is randomized (does not update dex) // Pokemon takes half max HP damage and nature is randomized (does not update dex)
applyDamageToPokemon(scene, chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 2)); applyDamageToPokemon(chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 2));
const currentNature = chosenPokemon.nature; const currentNature = chosenPokemon.nature;
let newNature = randSeedInt(25) as Nature; let newNature = randSeedInt(25) as Nature;
@ -140,8 +142,8 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
chosenPokemon.setCustomNature(newNature); chosenPokemon.setCustomNature(newNature);
encounter.setDialogueToken("newNature", getNatureName(newNature)); encounter.setDialogueToken("newNature", getNatureName(newNature));
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`); queueEncounterMessage(`${namespace}:cheap_side_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp([ chosenPokemon.id ], 100);
await chosenPokemon.updateInfo(); await chosenPokemon.updateInfo();
}) })
.build() .build()
@ -159,15 +161,15 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Update money // Update money
updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney); updatePlayerMoney(-(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
// Calculate modifiers and dialogue tokens // Calculate modifiers and dialogue tokens
const modifiers = [ const modifiers = [
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!, generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!, generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
]; ];
encounter.setDialogueToken("boost1", modifiers[0].name); encounter.setDialogueToken("boost1", modifiers[0].name);
encounter.setDialogueToken("boost2", modifiers[1].name); encounter.setDialogueToken("boost2", modifiers[1].name);
@ -179,30 +181,30 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
// Only Pokemon that can gain benefits are unfainted // Only Pokemon that can gain benefits are unfainted
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Choose Expensive Option // Choose Expensive Option
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
const modifiers = encounter.misc.modifiers; const modifiers = encounter.misc.modifiers;
for (const modType of modifiers) { for (const modType of modifiers) {
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); await applyModifierTypeToPlayerPokemon(chosenPokemon, modType);
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Status applied after dealer leaves (to make thematic sense) // Status applied after dealer leaves (to make thematic sense)
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
queueEncounterMessage(scene, `${namespace}:no_bad_effects`); queueEncounterMessage(`${namespace}:no_bad_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp([ chosenPokemon.id ], 100);
await chosenPokemon.updateInfo(); await chosenPokemon.updateInfo();
}) })
@ -219,9 +221,9 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
} }
] ]
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,13 +1,16 @@
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
@ -51,8 +54,8 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
console.log(encounter); console.log(encounter);
// Calculate boss mon // Calculate boss mon
@ -65,11 +68,11 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
moveSet: [ Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT ], moveSet: [ Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
stackCount: 2 stackCount: 2
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
stackCount: 2 stackCount: 2
}, },
], ],
@ -83,7 +86,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [ config ]; encounter.enemyPartyConfigs = [ config ];
// Load animations/sfx for Snorlax fight start moves // Load animations/sfx for Snorlax fight start moves
loadCustomMovesForEncounter(scene, [ Moves.SNORE ]); loadCustomMovesForEncounter([ Moves.SNORE ]);
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName()); encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
@ -103,10 +106,10 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: true });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -120,7 +123,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
move: new PokemonMove(Moves.SNORE), move: new PokemonMove(Moves.SNORE),
ignorePp: true ignorePp: true
}); });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -133,12 +136,12 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Fall asleep waiting for Snorlax // Fall asleep waiting for Snorlax
// Full heal party // Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true)); globalScene.unshiftPhase(new PartyHealPhase(true));
queueEncounterMessage(scene, `${namespace}:option.2.rest_result`); queueEncounterMessage(`${namespace}:option.2.rest_result`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withOption( .withOption(
@ -155,13 +158,13 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
} }
] ]
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Steal the Snorlax's Leftovers // Steal the Snorlax's Leftovers
const instance = scene.currentBattle.mysteryEncounter!; const instance = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: false });
// Snorlax exp to Pokemon that did the stealing // Snorlax exp to Pokemon that did the stealing
setEncounterExp(scene, instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp); setEncounterExp(instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )

View File

@ -1,10 +1,13 @@
import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon } from "#app/field/pokemon";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
@ -62,9 +65,9 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER); const price = globalScene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
encounter.misc = { encounter.misc = {
price price
@ -85,14 +88,14 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
} }
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Update money // Update money
updatePlayerMoney(scene, -scene.currentBattle.mysteryEncounter!.misc.price, true, false); updatePlayerMoney(-globalScene.currentBattle.mysteryEncounter!.misc.price, true, false);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene); const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit();
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -110,11 +113,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
} }
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene); const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit();
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
setEncounterExp(scene, scene.currentBattle.mysteryEncounter!.selectedOption!.primaryPokemon!.id, 100); setEncounterExp(globalScene.currentBattle.mysteryEncounter!.selectedOption!.primaryPokemon!.id, 100);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -128,14 +131,14 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Inspect the Machine // Inspect the Machine
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Init enemy // Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); const bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [{ pokemonConfigs: [{
@ -146,36 +149,36 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
}], }],
}; };
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!; const magnet = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!; const metalCoat = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
await transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(true, true);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
} }
) )
.build(); .build();
async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) { async function doBiomeTransitionDialogueAndBattleInit() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate new biome (cannot be current biome) // Calculate new biome (cannot be current biome)
const filteredBiomes = BIOME_CANDIDATES.filter(b => scene.arena.biomeType !== b); const filteredBiomes = BIOME_CANDIDATES.filter(b => globalScene.arena.biomeType !== b);
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)]; const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
// Show dialogue and transition biome // Show dialogue and transition biome
await showEncounterText(scene, `${namespace}:transport`); await showEncounterText(`${namespace}:transport`);
await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]); await Promise.all([ animateBiomeChange(newBiome), transitionMysteryEncounterIntroVisuals() ]);
scene.playBgm(); globalScene.playBgm();
await showEncounterText(scene, `${namespace}:attacked`); await showEncounterText(`${namespace}:attacked`);
// Init enemy // Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); const bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
// Defense/Spd buffs below wave 50, +1 to all stats otherwise // Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] : [ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
@ -187,8 +190,8 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
isBoss: true, isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`); queueEncounterMessage(`${namespace}:boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
}], }],
}; };
@ -196,46 +199,46 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
return config; return config;
} }
async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) { async function animateBiomeChange(nextBiome: Biome) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
scene.tweens.add({ globalScene.tweens.add({
targets: [ scene.arenaEnemy, scene.lastEnemyTrainer ], targets: [ globalScene.arenaEnemy, globalScene.lastEnemyTrainer ],
x: "+=300", x: "+=300",
duration: 2000, duration: 2000,
onComplete: () => { onComplete: () => {
scene.newArena(nextBiome); globalScene.newArena(nextBiome);
const biomeKey = getBiomeKey(nextBiome); const biomeKey = getBiomeKey(nextBiome);
const bgTexture = `${biomeKey}_bg`; const bgTexture = `${biomeKey}_bg`;
scene.arenaBgTransition.setTexture(bgTexture); globalScene.arenaBgTransition.setTexture(bgTexture);
scene.arenaBgTransition.setAlpha(0); globalScene.arenaBgTransition.setAlpha(0);
scene.arenaBgTransition.setVisible(true); globalScene.arenaBgTransition.setVisible(true);
scene.arenaPlayerTransition.setBiome(nextBiome); globalScene.arenaPlayerTransition.setBiome(nextBiome);
scene.arenaPlayerTransition.setAlpha(0); globalScene.arenaPlayerTransition.setAlpha(0);
scene.arenaPlayerTransition.setVisible(true); globalScene.arenaPlayerTransition.setVisible(true);
scene.tweens.add({ globalScene.tweens.add({
targets: [ scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition ], targets: [ globalScene.arenaPlayer, globalScene.arenaBgTransition, globalScene.arenaPlayerTransition ],
duration: 1000, duration: 1000,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
alpha: (target: any) => target === scene.arenaPlayer ? 0 : 1, alpha: (target: any) => target === globalScene.arenaPlayer ? 0 : 1,
onComplete: () => { onComplete: () => {
scene.arenaBg.setTexture(bgTexture); globalScene.arenaBg.setTexture(bgTexture);
scene.arenaPlayer.setBiome(nextBiome); globalScene.arenaPlayer.setBiome(nextBiome);
scene.arenaPlayer.setAlpha(1); globalScene.arenaPlayer.setAlpha(1);
scene.arenaEnemy.setBiome(nextBiome); globalScene.arenaEnemy.setBiome(nextBiome);
scene.arenaEnemy.setAlpha(1); globalScene.arenaEnemy.setAlpha(1);
scene.arenaNextEnemy.setBiome(nextBiome); globalScene.arenaNextEnemy.setBiome(nextBiome);
scene.arenaBgTransition.setVisible(false); globalScene.arenaBgTransition.setVisible(false);
scene.arenaPlayerTransition.setVisible(false); globalScene.arenaPlayerTransition.setVisible(false);
if (scene.lastEnemyTrainer) { if (globalScene.lastEnemyTrainer) {
scene.lastEnemyTrainer.destroy(); globalScene.lastEnemyTrainer.destroy();
} }
resolve(); resolve();
scene.tweens.add({ globalScene.tweens.add({
targets: scene.arenaEnemy, targets: globalScene.arenaEnemy,
x: "-=300", x: "-=300",
}); });
} }

View File

@ -1,9 +1,11 @@
import { EnemyPartyConfig, generateModifierType, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { generateModifierType, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs } from "#app/data/trainer-config"; import { trainerConfigs } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { randSeedShuffle } from "#app/utils"; import { randSeedShuffle } from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; import type MysteryEncounter from "../mystery-encounter";
import { MysteryEncounterBuilder } from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
@ -14,17 +16,18 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { IEggOptions } from "#app/data/egg"; import type { IEggOptions } from "#app/data/egg";
import { EggSourceType } from "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { getPokeballTintColor } from "#app/data/pokeball"; import { getPokeballTintColor } from "#app/data/pokeball";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/theExpertPokemonBreeder"; const namespace = "mysteryEncounters/theExpertPokemonBreeder";
@ -93,14 +96,14 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const waveIndex = scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
// Calculates what trainers are available for battle in the encounter // Calculates what trainers are available for battle in the encounter
// If player is in space biome, uses special "Space" version of the trainer // If player is in space biome, uses special "Space" version of the trainer
encounter.enemyPartyConfigs = [ encounter.enemyPartyConfigs = [
getPartyConfig(scene) getPartyConfig()
]; ];
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE; const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
@ -125,7 +128,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
]; ];
// Determine the 3 pokemon the player can battle with // Determine the 3 pokemon the player can battle with
let partyCopy = scene.getPlayerParty().slice(0); let partyCopy = globalScene.getPlayerParty().slice(0);
partyCopy = partyCopy partyCopy = partyCopy
.filter(p => p.isAllowedInBattle()) .filter(p => p.isAllowedInBattle())
.sort((a, b) => a.friendship - b.friendship); .sort((a, b) => a.friendship - b.friendship);
@ -139,7 +142,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
// Dialogue and egg calcs for Pokemon 1 // Dialogue and egg calcs for Pokemon 1
const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1); const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1);
let pokemon1Tooltip = getEncounterText(scene, `${namespace}:option.1.tooltip_base`)!; let pokemon1Tooltip = getEncounterText(`${namespace}:option.1.tooltip_base`)!;
if (pokemon1RareEggs > 0) { if (pokemon1RareEggs > 0) {
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText }); pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
@ -154,7 +157,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
// Dialogue and egg calcs for Pokemon 2 // Dialogue and egg calcs for Pokemon 2
const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2); const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2);
let pokemon2Tooltip = getEncounterText(scene, `${namespace}:option.2.tooltip_base`)!; let pokemon2Tooltip = getEncounterText(`${namespace}:option.2.tooltip_base`)!;
if (pokemon2RareEggs > 0) { if (pokemon2RareEggs > 0) {
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText }); pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
@ -169,7 +172,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
// Dialogue and egg calcs for Pokemon 3 // Dialogue and egg calcs for Pokemon 3
const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3); const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3);
let pokemon3Tooltip = getEncounterText(scene, `${namespace}:option.3.tooltip_base`)!; let pokemon3Tooltip = getEncounterText(`${namespace}:option.3.tooltip_base`)!;
if (pokemon3RareEggs > 0) { if (pokemon3RareEggs > 0) {
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") }); const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText }); pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
@ -212,22 +215,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn battle with first pokemon // Spawn battle with first pokemon
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc; const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc;
encounter.misc.chosenPokemon = pokemon1; encounter.misc.chosenPokemon = pokemon1;
encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender());
const eggOptions = getEggOptions(scene, pokemon1CommonEggs, pokemon1RareEggs); const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs);
setEncounterRewards(scene, setEncounterRewards(
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true }, { guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
eggOptions, eggOptions,
() => doPostEncounterCleanup(scene)); () => doPostEncounterCleanup());
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon1); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon1);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg rewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
@ -248,7 +251,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -264,22 +267,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn battle with second pokemon // Spawn battle with second pokemon
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc; const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc;
encounter.misc.chosenPokemon = pokemon2; encounter.misc.chosenPokemon = pokemon2;
encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender());
const eggOptions = getEggOptions(scene, pokemon2CommonEggs, pokemon2RareEggs); const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs);
setEncounterRewards(scene, setEncounterRewards(
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true }, { guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
eggOptions, eggOptions,
() => doPostEncounterCleanup(scene)); () => doPostEncounterCleanup());
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon2); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon2);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg rewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
@ -300,7 +303,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -316,22 +319,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn battle with third pokemon // Spawn battle with third pokemon
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc; const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc;
encounter.misc.chosenPokemon = pokemon3; encounter.misc.chosenPokemon = pokemon3;
encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender());
const eggOptions = getEggOptions(scene, pokemon3CommonEggs, pokemon3RareEggs); const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs);
setEncounterRewards(scene, setEncounterRewards(
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true }, { guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
eggOptions, eggOptions,
() => doPostEncounterCleanup(scene)); () => doPostEncounterCleanup());
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon3); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon3);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg rewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
@ -352,7 +355,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
} }
encounter.onGameOver = onGameOver; encounter.onGameOver = onGameOver;
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -364,9 +367,9 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
]) ])
.build(); .build();
function getPartyConfig(scene: BattleScene): EnemyPartyConfig { function getPartyConfig(): EnemyPartyConfig {
// Bug type superfan trainer config // Bug type superfan trainer config
const waveIndex = scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone(); const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone();
breederConfig.name = i18next.t(trainerNameKey); breederConfig.name = i18next.t(trainerNameKey);
@ -386,14 +389,14 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
ivs: [ 31, 31, 31, 31, 31, 31 ], ivs: [ 31, 31, 31, 31, 31, 31 ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType,
} }
] ]
} }
] ]
}; };
if (scene.arena.biomeType === Biome.SPACE) { if (globalScene.arena.biomeType === Biome.SPACE) {
// All 3 members always Cleffa line, but different configs // All 3 members always Cleffa line, but different configs
baseConfig.pokemonConfigs!.push({ baseConfig.pokemonConfigs!.push({
nickname: i18next.t(`${namespace}:cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }), nickname: i18next.t(`${namespace}:cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
@ -476,14 +479,13 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
return [ numCommons, numRares ]; return [ numCommons, numRares ];
} }
function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) { function getEggOptions(commonEggs: number, rareEggs: number) {
const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(trainerNameKey); const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(trainerNameKey);
const eggOptions: IEggOptions[] = []; const eggOptions: IEggOptions[] = [];
if (commonEggs > 0) { if (commonEggs > 0) {
for (let i = 0; i < commonEggs; i++) { for (let i = 0; i < commonEggs; i++) {
eggOptions.push({ eggOptions.push({
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: eggDescription, eggDescriptor: eggDescription,
@ -494,7 +496,6 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
if (rareEggs > 0) { if (rareEggs > 0) {
for (let i = 0; i < rareEggs; i++) { for (let i = 0; i < rareEggs; i++) {
eggOptions.push({ eggOptions.push({
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: eggDescription, eggDescriptor: eggDescription,
@ -506,36 +507,36 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
return eggOptions; return eggOptions;
} }
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) { function removePokemonFromPartyAndStoreHeldItems(encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
const chosenIndex = party.indexOf(chosenPokemon); const chosenIndex = party.indexOf(chosenPokemon);
party[chosenIndex] = party[0]; party[chosenIndex] = party[0];
party[0] = chosenPokemon; party[0] = chosenPokemon;
encounter.misc.originalParty = scene.getPlayerParty().slice(1); encounter.misc.originalParty = globalScene.getPlayerParty().slice(1);
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
.map(p => p.getHeldItems()); .map(p => p.getHeldItems());
scene["party"] = [ globalScene["party"] = [
chosenPokemon chosenPokemon
]; ];
} }
function restorePartyAndHeldItems(scene: BattleScene) { function restorePartyAndHeldItems() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Restore original party // Restore original party
scene.getPlayerParty().push(...encounter.misc.originalParty); globalScene.getPlayerParty().push(...encounter.misc.originalParty);
// Restore held items // Restore held items
const originalHeldItems = encounter.misc.originalPartyHeldItems; const originalHeldItems = encounter.misc.originalPartyHeldItems;
originalHeldItems.forEach((pokemonHeldItemsList: PokemonHeldItemModifier[]) => { originalHeldItems.forEach((pokemonHeldItemsList: PokemonHeldItemModifier[]) => {
pokemonHeldItemsList.forEach(heldItem => { pokemonHeldItemsList.forEach(heldItem => {
scene.addModifier(heldItem, true, false, false, true); globalScene.addModifier(heldItem, true, false, false, true);
}); });
}); });
scene.updateModifiers(true); globalScene.updateModifiers(true);
} }
function onGameOver(scene: BattleScene) { function onGameOver() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
@ -545,7 +546,7 @@ function onGameOver(scene: BattleScene) {
]; ];
// Restore original party, player loses all friendship with chosen mon (it remains fainted) // Restore original party, player loses all friendship with chosen mon (it remains fainted)
restorePartyAndHeldItems(scene); restorePartyAndHeldItems();
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
chosenPokemon.friendship = 0; chosenPokemon.friendship = 0;
@ -556,33 +557,33 @@ function onGameOver(scene: BattleScene) {
encounter.misc.encounterFailed = true; encounter.misc.encounterFailed = true;
// Revert BGM // Revert BGM
scene.playBgm(scene.arena.bgm); globalScene.playBgm(globalScene.arena.bgm);
// Clear any leftover battle phases // Clear any leftover battle phases
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.clearPhaseQueueSplice(); globalScene.clearPhaseQueueSplice();
// Return enemy Pokemon // Return enemy Pokemon
const pokemon = scene.getEnemyPokemon(); const pokemon = globalScene.getEnemyPokemon();
if (pokemon) { if (pokemon) {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokemon.hideInfo(); pokemon.hideInfo();
pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn"); pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn");
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 250, duration: 250,
ease: "Sine.easeIn", ease: "Sine.easeIn",
scale: 0.5, scale: 0.5,
onComplete: () => { onComplete: () => {
scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
} }
}); });
} }
// Show the enemy trainer // Show the enemy trainer
scene.time.delayedCall(250, () => { globalScene.time.delayedCall(250, () => {
const sprites = scene.currentBattle.trainer?.getSprites(); const sprites = globalScene.currentBattle.trainer?.getSprites();
const tintSprites = scene.currentBattle.trainer?.getTintSprites(); const tintSprites = globalScene.currentBattle.trainer?.getTintSprites();
if (sprites && tintSprites) { if (sprites && tintSprites) {
for (let i = 0; i < sprites.length; i++) { for (let i = 0; i < sprites.length; i++) {
sprites[i].setVisible(true); sprites[i].setVisible(true);
@ -591,8 +592,8 @@ function onGameOver(scene: BattleScene) {
tintSprites[i].clearTint(); tintSprites[i].clearTint();
} }
} }
scene.tweens.add({ globalScene.tweens.add({
targets: scene.currentBattle.trainer, targets: globalScene.currentBattle.trainer,
x: "-=16", x: "-=16",
y: "+=16", y: "+=16",
alpha: 1, alpha: 1,
@ -602,16 +603,16 @@ function onGameOver(scene: BattleScene) {
}); });
handleMysteryEncounterBattleFailed(scene, true); handleMysteryEncounterBattleFailed(true);
return false; return false;
} }
function doPostEncounterCleanup(scene: BattleScene) { function doPostEncounterCleanup() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (!encounter.misc.encounterFailed) { if (!encounter.misc.encounterFailed) {
// Give 20 friendship to the chosen pokemon // Give 20 friendship to the chosen pokemon
encounter.misc.chosenPokemon.addFriendship(FRIENDSHIP_ADDED); encounter.misc.chosenPokemon.addFriendship(FRIENDSHIP_ADDED);
restorePartyAndHeldItems(scene); restorePartyAndHeldItems();
} }
} }

View File

@ -1,15 +1,18 @@
import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { catchPokemon, getRandomSpeciesByStarterCost, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { catchPokemon, getRandomSpeciesByStarterCost, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import { PlayerPokemon } from "#app/field/pokemon";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
@ -58,8 +61,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
let species = getSalesmanSpeciesOffer(); let species = getSalesmanSpeciesOffer();
let tries = 0; let tries = 0;
@ -74,9 +77,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) { if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) {
// If no HA mon found or you roll 1%, give shiny Magikarp with random variant // If no HA mon found or you roll 1%, give shiny Magikarp with random variant
species = getPokemonSpecies(Species.MAGIKARP); species = getPokemonSpecies(Species.MAGIKARP);
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex, undefined, true); pokemon = new PlayerPokemon(species, 5, 2, species.formIndex, undefined, true);
} else { } else {
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex); pokemon = new PlayerPokemon(species, 5, 2, species.formIndex);
} }
pokemon.generateAndPopulateMoveset(); pokemon.generateAndPopulateMoveset();
@ -101,7 +104,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}:description_shiny`; encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}:description_shiny`;
encounter.options[0].dialogue!.buttonTooltip = `${namespace}:option.1.tooltip_shiny`; encounter.options[0].dialogue!.buttonTooltip = `${namespace}:option.1.tooltip_shiny`;
} }
const price = scene.getWaveMoneyAmount(priceMultiplier); const price = globalScene.getWaveMoneyAmount(priceMultiplier);
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender()); encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
encounter.misc = { encounter.misc = {
@ -127,24 +130,24 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
} }
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const price = encounter.misc.price; const price = encounter.misc.price;
const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon; const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon;
// Update money // Update money
updatePlayerMoney(scene, -price, true, false); updatePlayerMoney(-price, true, false);
// Show dialogue // Show dialogue
await showEncounterDialogue(scene, `${namespace}:option.1.selected_dialogue`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:option.1.selected_dialogue`, `${namespace}:speaker`);
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
// "Catch" purchased pokemon // "Catch" purchased pokemon
const data = new PokemonData(purchasedPokemon); const data = new PokemonData(purchasedPokemon);
data.player = false; data.player = false;
await catchPokemon(scene, data.toPokemon(scene) as EnemyPokemon, null, PokeballType.POKEBALL, true, true); await catchPokemon(data.toPokemon() as EnemyPokemon, null, PokeballType.POKEBALL, true, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -158,9 +161,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,12 +1,16 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type"; import { initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -67,8 +71,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
@ -85,26 +89,26 @@ export const TheStrongStuffEncounter: MysteryEncounter =
moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ], moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2 stackCount: 2
} }
], ],
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.stat_boost`); queueEncounterMessage(`${namespace}:option.2.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2));
} }
} }
], ],
@ -112,7 +116,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [ config ]; encounter.enemyPartyConfigs = [ config ];
loadCustomMovesForEncounter(scene, [ Moves.GASTRO_ACID, Moves.STEALTH_ROCK ]); loadCustomMovesForEncounter([ Moves.GASTRO_ACID, Moves.STEALTH_ROCK ]);
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName()); encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
@ -132,16 +136,16 @@ export const TheStrongStuffEncounter: MysteryEncounter =
} }
] ]
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Do blackout and hide intro visuals during blackout // Do blackout and hide intro visuals during blackout
scene.time.delayedCall(750, () => { globalScene.time.delayedCall(750, () => {
transitionMysteryEncounterIntroVisuals(scene, true, true, 50); transitionMysteryEncounterIntroVisuals(true, true, 50);
}); });
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP) // -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
// Sort party by bst // Sort party by bst
const sortedParty = scene.getPlayerParty().slice(0) const sortedParty = globalScene.getPlayerParty().slice(0)
.sort((pokemon1, pokemon2) => { .sort((pokemon1, pokemon2) => {
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0); const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0); const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
@ -161,15 +165,15 @@ export const TheStrongStuffEncounter: MysteryEncounter =
encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString()); encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString());
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString()); encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
await showEncounterText(scene, `${namespace}:option.1.selected_2`, null, undefined, true); await showEncounterText(`${namespace}:option.1.selected_2`, null, undefined, true);
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
text: `${namespace}:outro`, text: `${namespace}:outro`,
} }
]; ];
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
@ -183,10 +187,10 @@ export const TheStrongStuffEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SOUL_DEW ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SOUL_DEW ], fillRemaining: true });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -202,8 +206,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
}); });
encounter.dialogue.outro = []; encounter.dialogue.outro = [];
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
} }
) )
.build(); .build();

View File

@ -1,8 +1,11 @@
import { EnemyPartyConfig, generateModifierType, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { generateModifierType, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -83,15 +86,15 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Loaded back to front for pop() operations // Loaded back to front for pop() operations
encounter.enemyPartyConfigs.push(getVitoTrainerConfig(scene)); encounter.enemyPartyConfigs.push(getVitoTrainerConfig());
encounter.enemyPartyConfigs.push(getVickyTrainerConfig(scene)); encounter.enemyPartyConfigs.push(getVickyTrainerConfig());
encounter.enemyPartyConfigs.push(getViviTrainerConfig(scene)); encounter.enemyPartyConfigs.push(getViviTrainerConfig());
encounter.enemyPartyConfigs.push(getVictoriaTrainerConfig(scene)); encounter.enemyPartyConfigs.push(getVictoriaTrainerConfig());
encounter.enemyPartyConfigs.push(getVictorTrainerConfig(scene)); encounter.enemyPartyConfigs.push(getVictorTrainerConfig());
return true; return true;
}) })
@ -110,13 +113,13 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Spawn 5 trainer battles back to back with Macho Brace in rewards // Spawn 5 trainer battles back to back with Macho Brace in rewards
scene.currentBattle.mysteryEncounter!.doContinueEncounter = async (scene: BattleScene) => { globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = async () => {
await endTrainerBattleAndShowDialogue(scene); await endTrainerBattleAndShowDialogue();
}; };
await transitionMysteryEncounterIntroVisuals(scene, true, false); await transitionMysteryEncounterIntroVisuals(true, false);
await spawnNextTrainerOrEndEncounter(scene); await spawnNextTrainerOrEndEncounter();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -130,47 +133,47 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Refuse the challenge, they full heal the party and give the player a Rarer Candy // Refuse the challenge, they full heal the party and give the player a Rarer Candy
scene.unshiftPhase(new PartyHealPhase(scene, true)); globalScene.unshiftPhase(new PartyHealPhase(true));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.RARER_CANDY ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.RARER_CANDY ], fillRemaining: false });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.build(); .build();
async function spawnNextTrainerOrEndEncounter(scene: BattleScene) { async function spawnNextTrainerOrEndEncounter() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const nextConfig = encounter.enemyPartyConfigs.pop(); const nextConfig = encounter.enemyPartyConfigs.pop();
if (!nextConfig) { if (!nextConfig) {
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
await showEncounterDialogue(scene, `${namespace}:victory`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`);
// Give 10x Voucher // Give 10x Voucher
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier(); const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
await scene.addModifier(newModifier); await globalScene.addModifier(newModifier);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name })); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
await showEncounterDialogue(scene, `${namespace}:victory_2`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:victory_2`, `${namespace}:speaker`);
scene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in globalScene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in
const machoBrace = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!; const machoBrace = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
machoBrace.type.tier = ModifierTier.MASTER; machoBrace.type.tier = ModifierTier.MASTER;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ machoBrace ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ machoBrace ], fillRemaining: false });
encounter.doContinueEncounter = undefined; encounter.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, false, MysteryEncounterMode.NO_BATTLE); leaveEncounterWithoutBattle(false, MysteryEncounterMode.NO_BATTLE);
} else { } else {
await initBattleWithEnemyConfig(scene, nextConfig); await initBattleWithEnemyConfig(nextConfig);
} }
} }
function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> { function endTrainerBattleAndShowDialogue(): Promise<void> {
return new Promise(async resolve => { return new Promise(async resolve => {
if (scene.currentBattle.mysteryEncounter!.enemyPartyConfigs.length === 0) { if (globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs.length === 0) {
// Battle is over // Battle is over
const trainer = scene.currentBattle.trainer; const trainer = globalScene.currentBattle.trainer;
if (trainer) { if (trainer) {
scene.tweens.add({ globalScene.tweens.add({
targets: trainer, targets: trainer,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -178,38 +181,38 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 750, duration: 750,
onComplete: () => { onComplete: () => {
scene.field.remove(trainer, true); globalScene.field.remove(trainer, true);
} }
}); });
} }
await spawnNextTrainerOrEndEncounter(scene); await spawnNextTrainerOrEndEncounter();
resolve(); // Wait for all dialogue/post battle stuff to complete before resolving resolve(); // Wait for all dialogue/post battle stuff to complete before resolving
} else { } else {
scene.arena.resetArenaEffects(); globalScene.arena.resetArenaEffects();
const playerField = scene.getPlayerField(); const playerField = globalScene.getPlayerField();
playerField.forEach((pokemon) => pokemon.lapseTag(BattlerTagType.COMMANDED)); playerField.forEach((pokemon) => pokemon.lapseTag(BattlerTagType.COMMANDED));
playerField.forEach((_, p) => scene.unshiftPhase(new ReturnPhase(scene, p))); playerField.forEach((_, p) => globalScene.unshiftPhase(new ReturnPhase(p)));
for (const pokemon of scene.getPlayerParty()) { for (const pokemon of globalScene.getPlayerParty()) {
// Only trigger form change when Eiscue is in Noice form // Only trigger form change when Eiscue is in Noice form
// Hardcoded Eiscue for now in case it is fused with another pokemon // Hardcoded Eiscue for now in case it is fused with another pokemon
if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) { if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) {
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger);
} }
pokemon.resetBattleData(); pokemon.resetBattleData();
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
} }
scene.unshiftPhase(new ShowTrainerPhase(scene)); globalScene.unshiftPhase(new ShowTrainerPhase());
// Hide the trainer and init next battle // Hide the trainer and init next battle
const trainer = scene.currentBattle.trainer; const trainer = globalScene.currentBattle.trainer;
// Unassign previous trainer from battle so it isn't destroyed before animation completes // Unassign previous trainer from battle so it isn't destroyed before animation completes
scene.currentBattle.trainer = null; globalScene.currentBattle.trainer = null;
await spawnNextTrainerOrEndEncounter(scene); await spawnNextTrainerOrEndEncounter();
if (trainer) { if (trainer) {
scene.tweens.add({ globalScene.tweens.add({
targets: trainer, targets: trainer,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -217,7 +220,7 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 750, duration: 750,
onComplete: () => { onComplete: () => {
scene.field.remove(trainer, true); globalScene.field.remove(trainer, true);
resolve(); resolve();
} }
}); });
@ -226,7 +229,7 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
}); });
} }
function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig { function getVictorTrainerConfig(): EnemyPartyConfig {
return { return {
trainerType: TrainerType.VICTOR, trainerType: TrainerType.VICTOR,
pokemonConfigs: [ pokemonConfigs: [
@ -238,11 +241,11 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK ], moveSet: [ Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.FOCUS_BAND) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.FOCUS_BAND) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
}, },
@ -256,11 +259,11 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH ], moveSet: [ Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
} }
@ -270,7 +273,7 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
}; };
} }
function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig { function getVictoriaTrainerConfig(): EnemyPartyConfig {
return { return {
trainerType: TrainerType.VICTORIA, trainerType: TrainerType.VICTORIA,
pokemonConfigs: [ pokemonConfigs: [
@ -282,11 +285,11 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER ], moveSet: [ Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
} }
@ -300,12 +303,12 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP ], moveSet: [ Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.PSYCHIC ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.PSYCHIC ]) as PokemonHeldItemModifierType,
stackCount: 1, stackCount: 1,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FAIRY ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FAIRY ]) as PokemonHeldItemModifierType,
stackCount: 1, stackCount: 1,
isTransferable: false isTransferable: false
} }
@ -315,7 +318,7 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
}; };
} }
function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig { function getViviTrainerConfig(): EnemyPartyConfig {
return { return {
trainerType: TrainerType.VIVI, trainerType: TrainerType.VIVI,
pokemonConfigs: [ pokemonConfigs: [
@ -327,12 +330,12 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST ], moveSet: [ Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
stackCount: 4, stackCount: 4,
isTransferable: false isTransferable: false
} }
@ -346,12 +349,12 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH ], moveSet: [ Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
stackCount: 4, stackCount: 4,
isTransferable: false isTransferable: false
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
isTransferable: false isTransferable: false
} }
] ]
@ -364,7 +367,7 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT ], moveSet: [ Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
stackCount: 3, stackCount: 3,
isTransferable: false isTransferable: false
}, },
@ -374,7 +377,7 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
}; };
} }
function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig { function getVickyTrainerConfig(): EnemyPartyConfig {
return { return {
trainerType: TrainerType.VICKY, trainerType: TrainerType.VICKY,
pokemonConfigs: [ pokemonConfigs: [
@ -386,7 +389,7 @@ function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH ], moveSet: [ Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
isTransferable: false isTransferable: false
} }
] ]
@ -395,7 +398,7 @@ function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
}; };
} }
function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig { function getVitoTrainerConfig(): EnemyPartyConfig {
return { return {
trainerType: TrainerType.VITO, trainerType: TrainerType.VITO,
pokemonConfigs: [ pokemonConfigs: [
@ -407,7 +410,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE ], moveSet: [ Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.SPD ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.SPD ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
} }
@ -421,47 +424,47 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE ], moveSet: [ Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.STARF ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.STARF ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SALAC ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SALAC ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LANSAT ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LANSAT ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LIECHI ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LIECHI ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.PETAYA ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.PETAYA ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
}, },
{ {
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LEPPA ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LEPPA ]) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
} }
] ]
@ -474,7 +477,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF ], moveSet: [ Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
} }
@ -488,7 +491,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT ], moveSet: [ Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
}, },
@ -502,7 +505,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
moveSet: [ Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE ], moveSet: [ Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE ],
modifierConfigs: [ modifierConfigs: [
{ {
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
stackCount: 2, stackCount: 2,
isTransferable: false isTransferable: false
}, },

View File

@ -1,22 +1,26 @@
import { Ability, allAbilities } from "#app/data/ability"; import type { Ability } from "#app/data/ability";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { allAbilities } from "#app/data/ability";
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getNatureName } from "#app/data/nature"; import { getNatureName } from "#app/data/nature";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import type Pokemon from "#app/field/pokemon";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { AbilityAttr } from "#app/system/game-data"; import { AbilityAttr } from "#app/system/game-data";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { isNullOrUndefined, randSeedShuffle } from "#app/utils"; import { isNullOrUndefined, randSeedShuffle } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import i18next from "i18next"; import i18next from "i18next";
import { getStatKey } from "#enums/stat"; import { getStatKey } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -71,8 +75,8 @@ export const TrainingSessionEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.misc = { encounter.misc = {
playerPokemon: pokemon, playerPokemon: pokemon,
@ -81,24 +85,24 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
// Spawn light training session with chosen pokemon // Spawn light training session with chosen pokemon
// Every 50 waves, add +1 boss segment, capping at 5 // Every 50 waves, add +1 boss segment, capping at 5
const segments = Math.min( const segments = Math.min(
2 + Math.floor(scene.currentBattle.waveIndex / 50), 2 + Math.floor(globalScene.currentBattle.waveIndex / 50),
5 5
); );
const modifiers = new ModifiersHolder(); const modifiers = new ModifiersHolder();
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); const config = getEnemyConfig(playerPokemon, segments, modifiers);
scene.removePokemonFromPlayerParty(playerPokemon, false); globalScene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
encounter.setDialogueToken("stat1", "-"); encounter.setDialogueToken("stat1", "-");
@ -148,23 +152,23 @@ export const TrainingSessionEncounter: MysteryEncounter =
if (improvedCount > 0) { if (improvedCount > 0) {
playerPokemon.calculateStats(); playerPokemon.calculateStats();
scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
scene.gameData.setPokemonCaught(playerPokemon, false); globalScene.gameData.setPokemonCaught(playerPokemon, false);
} }
// Add pokemon and mods back // Add pokemon and mods back
scene.getPlayerParty().push(playerPokemon); globalScene.getPlayerParty().push(playerPokemon);
for (const mod of modifiers.value) { for (const mod of modifiers.value) {
mod.pokemonId = playerPokemon.id; mod.pokemonId = playerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
scene.updateModifiers(true); globalScene.updateModifiers(true);
queueEncounterMessage(scene, `${namespace}:option.1.finished`); queueEncounterMessage(`${namespace}:option.1.finished`);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -182,15 +186,15 @@ export const TrainingSessionEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
// Open menu for selecting pokemon and Nature // Open menu for selecting pokemon and Nature
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const natures = new Array(25).fill(null).map((val, i) => i as Nature); const natures = new Array(25).fill(null).map((val, i) => i as Nature);
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for nature selection // Return the options for nature selection
return natures.map((nature: Nature) => { return natures.map((nature: Nature) => {
const option: OptionSelectItem = { const option: OptionSelectItem = {
label: getNatureName(nature, true, true, true, scene.uiTheme), label: getNatureName(nature, true, true, true, globalScene.uiTheme),
handler: () => { handler: () => {
// Pokemon and second option selected // Pokemon and second option selected
encounter.setDialogueToken("nature", getNatureName(nature)); encounter.setDialogueToken("nature", getNatureName(nature));
@ -207,40 +211,40 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
// Spawn medium training session with chosen pokemon // Spawn medium training session with chosen pokemon
// Every 40 waves, add +1 boss segment, capping at 6 // Every 40 waves, add +1 boss segment, capping at 6
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6); const segments = Math.min(2 + Math.floor(globalScene.currentBattle.waveIndex / 40), 6);
const modifiers = new ModifiersHolder(); const modifiers = new ModifiersHolder();
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); const config = getEnemyConfig(playerPokemon, segments, modifiers);
scene.removePokemonFromPlayerParty(playerPokemon, false); globalScene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}:option.2.finished`); queueEncounterMessage(`${namespace}:option.2.finished`);
// Add the pokemon back to party with Nature change // Add the pokemon back to party with Nature change
playerPokemon.setCustomNature(encounter.misc.chosenNature); playerPokemon.setCustomNature(encounter.misc.chosenNature);
scene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature); globalScene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature);
// Add pokemon and modifiers back // Add pokemon and modifiers back
scene.getPlayerParty().push(playerPokemon); globalScene.getPlayerParty().push(playerPokemon);
for (const mod of modifiers.value) { for (const mod of modifiers.value) {
mod.pokemonId = playerPokemon.id; mod.pokemonId = playerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
scene.updateModifiers(true); globalScene.updateModifiers(true);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -258,9 +262,9 @@ export const TrainingSessionEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
// Open menu for selecting pokemon and ability to learn // Open menu for selecting pokemon and ability to learn
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for ability selection // Return the options for ability selection
const speciesForm = !!pokemon.getFusionSpeciesForm() const speciesForm = !!pokemon.getFusionSpeciesForm()
@ -286,7 +290,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
return true; return true;
}, },
onHover: () => { onHover: () => {
showEncounterText(scene, ability.description, 0, 0, false); showEncounterText(ability.description, 0, 0, false);
}, },
}; };
optionSelectItems.push(option); optionSelectItems.push(option);
@ -298,28 +302,28 @@ export const TrainingSessionEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be trained // Only Pokemon that are not KOed/legal can be trained
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon; const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
// Spawn hard training session with chosen pokemon // Spawn hard training session with chosen pokemon
// Every 30 waves, add +1 boss segment, capping at 6 // Every 30 waves, add +1 boss segment, capping at 6
// Also starts with +1 to all stats // Also starts with +1 to all stats
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6); const segments = Math.min(2 + Math.floor(globalScene.currentBattle.waveIndex / 30), 6);
const modifiers = new ModifiersHolder(); const modifiers = new ModifiersHolder();
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers); const config = getEnemyConfig(playerPokemon, segments, modifiers);
config.pokemonConfigs![0].tags = [ config.pokemonConfigs![0].tags = [
BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON,
]; ];
scene.removePokemonFromPlayerParty(playerPokemon, false); globalScene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}:option.3.finished`); queueEncounterMessage(`${namespace}:option.3.finished`);
// Add the pokemon back to party with ability change // Add the pokemon back to party with ability change
const abilityIndex = encounter.misc.abilityIndex; const abilityIndex = encounter.misc.abilityIndex;
@ -330,8 +334,8 @@ export const TrainingSessionEncounter: MysteryEncounter =
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId(); const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
if (!isNullOrUndefined(rootFusionSpecies) if (!isNullOrUndefined(rootFusionSpecies)
&& speciesStarterCosts.hasOwnProperty(rootFusionSpecies) && speciesStarterCosts.hasOwnProperty(rootFusionSpecies)
&& !!scene.gameData.dexData[rootFusionSpecies].caughtAttr) { && !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr) {
scene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2 globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
? 1 << playerPokemon.fusionAbilityIndex ? 1 << playerPokemon.fusionAbilityIndex
: AbilityAttr.ABILITY_HIDDEN; : AbilityAttr.ABILITY_HIDDEN;
} }
@ -340,20 +344,20 @@ export const TrainingSessionEncounter: MysteryEncounter =
} }
playerPokemon.calculateStats(); playerPokemon.calculateStats();
scene.gameData.setPokemonCaught(playerPokemon, false); globalScene.gameData.setPokemonCaught(playerPokemon, false);
// Add pokemon and mods back // Add pokemon and mods back
scene.getPlayerParty().push(playerPokemon); globalScene.getPlayerParty().push(playerPokemon);
for (const mod of modifiers.value) { for (const mod of modifiers.value) {
mod.pokemonId = playerPokemon.id; mod.pokemonId = playerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
scene.updateModifiers(true); globalScene.updateModifiers(true);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase); setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -367,15 +371,15 @@ export const TrainingSessionEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig { function getEnemyConfig(playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {
playerPokemon.resetSummonData(); playerPokemon.resetSummonData();
// Passes modifiers by reference // Passes modifiers by reference

View File

@ -1,8 +1,11 @@
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig, EnemyPokemonConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -58,8 +61,8 @@ export const TrashToTreasureEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon (shiny locked) // Calculate boss mon (shiny locked)
const bossSpecies = getPokemonSpecies(Species.GARBODOR); const bossSpecies = getPokemonSpecies(Species.GARBODOR);
@ -79,10 +82,10 @@ export const TrashToTreasureEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [ config ]; encounter.enemyPartyConfigs = [ config ];
// Load animations/sfx for Garbodor fight start moves // Load animations/sfx for Garbodor fight start moves
loadCustomMovesForEncounter(scene, [ Moves.TOXIC, Moves.AMNESIA ]); loadCustomMovesForEncounter([ Moves.TOXIC, Moves.AMNESIA ]);
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav"); globalScene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav"); globalScene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
encounter.setDialogueToken("costMultiplier", SHOP_ITEM_COST_MULTIPLIER.toString()); encounter.setDialogueToken("costMultiplier", SHOP_ITEM_COST_MULTIPLIER.toString());
@ -100,24 +103,24 @@ export const TrashToTreasureEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Play Dig2 and then Venom Drench sfx // Play Dig2 and then Venom Drench sfx
doGarbageDig(scene); doGarbageDig();
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Gain 2 Leftovers and 2 Shell Bell // Gain 2 Leftovers and 2 Shell Bell
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
await tryApplyDigRewardItems(scene); await tryApplyDigRewardItems();
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]); const blackSludge = generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
const modifier = blackSludge?.newModifier(); const modifier = blackSludge?.newModifier();
if (modifier) { if (modifier) {
await scene.addModifier(modifier, false, false, false, true); await globalScene.addModifier(modifier, false, false, false, true);
scene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 }); globalScene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: modifier.type.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: modifier.type.name }), null, undefined, true);
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -133,15 +136,15 @@ export const TrashToTreasureEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Investigate garbage, battle Gmax Garbodor // Investigate garbage, battle Gmax Garbodor
scene.setFieldScale(0.75); globalScene.setFieldScale(0.75);
await showEncounterText(scene, `${namespace}:option.2.selected_2`); await showEncounterText(`${namespace}:option.2.selected_2`);
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -155,81 +158,81 @@ export const TrashToTreasureEncounter: MysteryEncounter =
move: new PokemonMove(Moves.AMNESIA), move: new PokemonMove(Moves.AMNESIA),
ignorePp: true ignorePp: true
}); });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
) )
.build(); .build();
async function tryApplyDigRewardItems(scene: BattleScene) { async function tryApplyDigRewardItems() {
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
const leftovers = generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType; const leftovers = generateModifierType(modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
// Iterate over the party until an item was successfully given // Iterate over the party until an item was successfully given
// First leftovers // First leftovers
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[]; && m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier; const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) { if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers); await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
break; break;
} }
} }
// Second leftovers // Second leftovers
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[]; && m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier; const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) { if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers); await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
break; break;
} }
} }
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: leftovers.name, count: 2 }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGainCount", { modifierName: leftovers.name, count: 2 }), null, undefined, true);
// First Shell bell // First Shell bell
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[]; && m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier; const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) { if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell); await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
break; break;
} }
} }
// Second Shell bell // Second Shell bell
for (const pokemon of party) { for (const pokemon of party) {
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[]; && m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier; const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) { if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell); await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
break; break;
} }
} }
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true);
} }
function doGarbageDig(scene: BattleScene) { function doGarbageDig() {
scene.playSound("battle_anims/PRSFX- Dig2"); globalScene.playSound("battle_anims/PRSFX- Dig2");
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => { globalScene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
scene.playSound("battle_anims/PRSFX- Dig2"); globalScene.playSound("battle_anims/PRSFX- Dig2");
scene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 }); globalScene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
}); });
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME * 2, () => { globalScene.time.delayedCall(SOUND_EFFECT_WAIT_TIME * 2, () => {
scene.playSound("battle_anims/PRSFX- Dig2"); globalScene.playSound("battle_anims/PRSFX- Dig2");
}); });
} }

View File

@ -1,11 +1,14 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import { getPartyLuckValue } from "#app/modifier/modifier-type"; import { getPartyLuckValue } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoveRequirement, PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement, PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -13,7 +16,7 @@ import { TrainerSlot } from "#app/data/trainer-config";
import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils"; import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { SelfStatusMove } from "#app/data/move"; import { SelfStatusMove } from "#app/data/move";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
@ -23,7 +26,8 @@ import { BerryModifier } from "#app/modifier/modifier";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/uncommonBreed"; const namespace = "mysteryEncounters/uncommonBreed";
@ -46,21 +50,21 @@ export const UncommonBreedEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
// Level equal to 2 below highest party member // Level equal to 2 below highest party member
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2; const level = getHighestLevelPlayerPokemon(false, true).level - 2;
let species: PokemonSpecies; let species: PokemonSpecies;
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
species = getPokemonSpecies( levelSpecies ); species = getPokemonSpecies( levelSpecies );
} else { } else {
species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); species = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
} }
const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true); const pokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, true);
// Pokemon will always have one of its egg moves in its moveset // Pokemon will always have one of its egg moves in its moveset
const eggMoves = pokemon.getEggMoves(); const eggMoves = pokemon.getEggMoves();
@ -81,7 +85,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
} }
// Defense/Spd buffs below wave 50, +1 to all stats otherwise // Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] : [ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
@ -93,8 +97,8 @@ export const UncommonBreedEncounter: MysteryEncounter =
isBoss: false, isBoss: false,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`); queueEncounterMessage(`${namespace}:option.1.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
}], }],
}; };
@ -115,16 +119,16 @@ export const UncommonBreedEncounter: MysteryEncounter =
]; ];
encounter.setDialogueToken("enemyPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("enemyPokemon", pokemon.getNameToRender());
scene.loadSe("PRSFX- Spotlight2", "battle_anims", "PRSFX- Spotlight2.wav"); globalScene.loadSe("PRSFX- Spotlight2", "battle_anims", "PRSFX- Spotlight2.wav");
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
// Animate the pokemon // Animate the pokemon
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemonSprite = encounter.introVisuals!.getSprites(); const pokemonSprite = encounter.introVisuals!.getSprites();
// Bounce at the end, then shiny sparkle if the Pokemon is shiny // Bounce at the end, then shiny sparkle if the Pokemon is shiny
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonSprite, targets: pokemonSprite,
duration: 300, duration: 300,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
@ -134,7 +138,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
onComplete: () => encounter.introVisuals?.playShinySparkles() onComplete: () => encounter.introVisuals?.playShinySparkles()
}); });
scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2")); globalScene.time.delayedCall(500, () => globalScene.playSound("battle_anims/PRSFX- Spotlight2"));
return true; return true;
}) })
.setLocalizationKey(`${namespace}`) .setLocalizationKey(`${namespace}`)
@ -151,9 +155,9 @@ export const UncommonBreedEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const eggMove = encounter.misc.eggMove; const eggMove = encounter.misc.eggMove;
if (!isNullOrUndefined(eggMove)) { if (!isNullOrUndefined(eggMove)) {
@ -171,8 +175,8 @@ export const UncommonBreedEncounter: MysteryEncounter =
}); });
} }
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
} }
) )
.withOption( .withOption(
@ -189,33 +193,33 @@ export const UncommonBreedEncounter: MysteryEncounter =
} }
] ]
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give it some food // Give it some food
// Remove 4 random berries from player's party // Remove 4 random berries from player's party
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
const berryItems: BerryModifier[] = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems: BerryModifier[] = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
const index = randSeedInt(berryItems.length); const index = randSeedInt(berryItems.length);
const randBerry = berryItems[index]; const randBerry = berryItems[index];
randBerry.stackCount--; randBerry.stackCount--;
if (randBerry.stackCount === 0) { if (randBerry.stackCount === 0) {
scene.removeModifier(randBerry); globalScene.removeModifier(randBerry);
berryItems.splice(index, 1); berryItems.splice(index, 1);
} }
} }
await scene.updateModifiers(true, true); await globalScene.updateModifiers(true, true);
// Pokemon joins the team, with 2 egg moves // Pokemon joins the team, with 2 egg moves
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = encounter.misc.pokemon; const pokemon = encounter.misc.pokemon;
// Give 1 additional egg move // Give 1 additional egg move
givePokemonExtraEggMove(pokemon, encounter.misc.eggMove); givePokemonExtraEggMove(pokemon, encounter.misc.eggMove);
await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false); await catchPokemon(pokemon, null, PokeballType.POKEBALL, false);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -233,10 +237,10 @@ export const UncommonBreedEncounter: MysteryEncounter =
} }
] ]
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Attract the pokemon with a move // Attract the pokemon with a move
// Pokemon joins the team, with 2 egg moves and IVs rolled an additional time // Pokemon joins the team, with 2 egg moves and IVs rolled an additional time
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = encounter.misc.pokemon; const pokemon = encounter.misc.pokemon;
// Give 1 additional egg move // Give 1 additional egg move
@ -248,12 +252,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
return newValue > iv ? newValue : iv; return newValue > iv ? newValue : iv;
}); });
await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false); await catchPokemon(pokemon, null, PokeballType.POKEBALL, false);
if (encounter.selectedOption?.primaryPokemon?.id) { if (encounter.selectedOption?.primaryPokemon?.id) {
setEncounterExp(scene, encounter.selectedOption.primaryPokemon.id, pokemon.getExpValue(), false); setEncounterExp(encounter.selectedOption.primaryPokemon.id, pokemon.getExpValue(), false);
} }
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )

View File

@ -1,20 +1,27 @@
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils"; import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence"; import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
import { getLevelTotalExp } from "#app/data/exp"; import { getLevelTotalExp } from "#app/data/exp";
@ -25,7 +32,7 @@ import { PlayerGender } from "#enums/player-gender";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { trainerConfigs, TrainerPartyTemplate } from "#app/data/trainer-config"; import { trainerConfigs, TrainerPartyTemplate } from "#app/data/trainer-config";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
@ -138,21 +145,21 @@ export const WeirdDreamEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3"); globalScene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
// Calculate all the newly transformed Pokemon and begin asset load // Calculate all the newly transformed Pokemon and begin asset load
const teamTransformations = getTeamTransformations(scene); const teamTransformations = getTeamTransformations();
const loadAssets = teamTransformations.map(t => (t.newPokemon as PlayerPokemon).loadAssets()); const loadAssets = teamTransformations.map(t => (t.newPokemon as PlayerPokemon).loadAssets());
scene.currentBattle.mysteryEncounter!.misc = { globalScene.currentBattle.mysteryEncounter!.misc = {
teamTransformations, teamTransformations,
loadAssets loadAssets
}; };
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm("mystery_encounter_weird_dream"); globalScene.fadeAndSwitchBgm("mystery_encounter_weird_dream");
return true; return true;
}) })
.withOption( .withOption(
@ -168,25 +175,25 @@ export const WeirdDreamEncounter: MysteryEncounter =
} }
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Play the animation as the player goes through the dialogue // Play the animation as the player goes through the dialogue
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
doShowDreamBackground(scene); doShowDreamBackground();
}); });
for (const transformation of scene.currentBattle.mysteryEncounter!.misc.teamTransformations) { for (const transformation of globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations) {
scene.removePokemonFromPlayerParty(transformation.previousPokemon, false); globalScene.removePokemonFromPlayerParty(transformation.previousPokemon, false);
scene.getPlayerParty().push(transformation.newPokemon); globalScene.getPlayerParty().push(transformation.newPokemon);
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue // Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue
const cutsceneDialoguePromise = showEncounterText(scene, `${namespace}:option.1.cutscene`); const cutsceneDialoguePromise = showEncounterText(`${namespace}:option.1.cutscene`);
// Change the entire player's party // Change the entire player's party
// Wait for all new Pokemon assets to be loaded before showing transformation animations // Wait for all new Pokemon assets to be loaded before showing transformation animations
await Promise.all(scene.currentBattle.mysteryEncounter!.misc.loadAssets); await Promise.all(globalScene.currentBattle.mysteryEncounter!.misc.loadAssets);
const transformations = scene.currentBattle.mysteryEncounter!.misc.teamTransformations; const transformations = globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
// If there are 1-3 transformations, do them centered back to back // If there are 1-3 transformations, do them centered back to back
// Otherwise, the first 3 transformations are executed side-by-side, then any remaining 1-3 transformations occur in those same respective positions // Otherwise, the first 3 transformations are executed side-by-side, then any remaining 1-3 transformations occur in those same respective positions
@ -195,21 +202,21 @@ export const WeirdDreamEncounter: MysteryEncounter =
const pokemon1 = transformation.previousPokemon; const pokemon1 = transformation.previousPokemon;
const pokemon2 = transformation.newPokemon; const pokemon2 = transformation.newPokemon;
await doPokemonTransformationSequence(scene, pokemon1, pokemon2, TransformationScreenPosition.CENTER); await doPokemonTransformationSequence(pokemon1, pokemon2, TransformationScreenPosition.CENTER);
} }
} else { } else {
await doSideBySideTransformations(scene, transformations); await doSideBySideTransformations(transformations);
} }
// Make sure player has finished cutscene dialogue // Make sure player has finished cutscene dialogue
await cutsceneDialoguePromise; await cutsceneDialoguePromise;
doHideDreamBackground(scene); doHideDreamBackground();
await showEncounterText(scene, `${namespace}:option.1.dream_complete`); await showEncounterText(`${namespace}:option.1.dream_complete`);
await doNewTeamPostProcess(scene, transformations); await doNewTeamPostProcess(transformations);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT, modifierTypes.MINT ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT, modifierTypes.MINT ], fillRemaining: false });
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -223,9 +230,9 @@ export const WeirdDreamEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Battle your "future" team for some item rewards // Battle your "future" team for some item rewards
const transformations: PokemonTransformation[] = scene.currentBattle.mysteryEncounter!.misc.teamTransformations; const transformations: PokemonTransformation[] = globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
// Uses the pokemon that player's party would have transformed into // Uses the pokemon that player's party would have transformed into
const enemyPokemonConfigs: EnemyPokemonConfig[] = []; const enemyPokemonConfigs: EnemyPokemonConfig[] = [];
@ -233,7 +240,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
const newPokemon = transformation.newPokemon; const newPokemon = transformation.newPokemon;
const previousPokemon = transformation.previousPokemon; const previousPokemon = transformation.previousPokemon;
await postProcessTransformedPokemon(scene, previousPokemon, newPokemon, newPokemon.species.getRootSpeciesId(), true); await postProcessTransformedPokemon(previousPokemon, newPokemon, newPokemon.species.getRootSpeciesId(), true);
const dataSource = new PokemonData(newPokemon); const dataSource = new PokemonData(newPokemon);
dataSource.player = false; dataSource.player = false;
@ -251,7 +258,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
if (shouldGetOldGateau(newPokemon)) { if (shouldGetOldGateau(newPokemon)) {
const stats = getOldGateauBoostedStats(newPokemon); const stats = getOldGateauBoostedStats(newPokemon);
newPokemonHeldItemConfigs.push({ newPokemonHeldItemConfigs.push({
modifier: generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU, [ OLD_GATEAU_STATS_UP, stats ]) as PokemonHeldItemModifierType, modifier: generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU, [ OLD_GATEAU_STATS_UP, stats ]) as PokemonHeldItemModifierType,
stackCount: 1, stackCount: 1,
isTransferable: false isTransferable: false
}); });
@ -268,7 +275,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
enemyPokemonConfigs.push(enemyConfig); enemyPokemonConfigs.push(enemyConfig);
} }
const genderIndex = scene.gameData.gender ?? PlayerGender.UNSET; const genderIndex = globalScene.gameData.gender ?? PlayerGender.UNSET;
const trainerConfig = trainerConfigs[genderIndex === PlayerGender.FEMALE ? TrainerType.FUTURE_SELF_F : TrainerType.FUTURE_SELF_M].clone(); const trainerConfig = trainerConfigs[genderIndex === PlayerGender.FEMALE ? TrainerType.FUTURE_SELF_F : TrainerType.FUTURE_SELF_M].clone();
trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG)); trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG));
const enemyPartyConfig: EnemyPartyConfig = { const enemyPartyConfig: EnemyPartyConfig = {
@ -280,7 +287,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
const onBeforeRewards = () => { const onBeforeRewards = () => {
// Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently) // Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently)
// One random pokemon will get its passive unlocked // One random pokemon will get its passive unlocked
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive); const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive);
if (passiveDisabledPokemon?.length > 0) { if (passiveDisabledPokemon?.length > 0) {
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
enablePassiveMon.passive = true; enablePassiveMon.passive = true;
@ -288,10 +295,10 @@ export const WeirdDreamEncounter: MysteryEncounter =
} }
}; };
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: false }, undefined, onBeforeRewards); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: false }, undefined, onBeforeRewards);
await showEncounterText(scene, `${namespace}:option.2.selected_2`, null, undefined, true); await showEncounterText(`${namespace}:option.2.selected_2`, null, undefined, true);
await initBattleWithEnemyConfig(scene, enemyPartyConfig); await initBattleWithEnemyConfig(enemyPartyConfig);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -304,9 +311,9 @@ export const WeirdDreamEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave, reduce party levels by 10% // Leave, reduce party levels by 10%
for (const pokemon of scene.getPlayerParty()) { for (const pokemon of globalScene.getPlayerParty()) {
pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1); pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1);
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate); pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
pokemon.levelExp = 0; pokemon.levelExp = 0;
@ -316,7 +323,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
await pokemon.updateInfo(); await pokemon.updateInfo();
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
@ -329,8 +336,8 @@ interface PokemonTransformation {
heldItems: PokemonHeldItemModifier[]; heldItems: PokemonHeldItemModifier[];
} }
function getTeamTransformations(scene: BattleScene): PokemonTransformation[] { function getTeamTransformations(): PokemonTransformation[] {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
// Removes all pokemon from the party // Removes all pokemon from the party
const alreadyUsedSpecies: PokemonSpecies[] = party.map(p => p.species); const alreadyUsedSpecies: PokemonSpecies[] = party.map(p => p.species);
const pokemonTransformations: PokemonTransformation[] = party.map(p => { const pokemonTransformations: PokemonTransformation[] = party.map(p => {
@ -379,37 +386,37 @@ function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
for (const transformation of pokemonTransformations) { for (const transformation of pokemonTransformations) {
const newAbilityIndex = randSeedInt(transformation.newSpecies.getAbilityCount()); const newAbilityIndex = randSeedInt(transformation.newSpecies.getAbilityCount());
transformation.newPokemon = scene.addPlayerPokemon(transformation.newSpecies, transformation.previousPokemon.level, newAbilityIndex, undefined); transformation.newPokemon = globalScene.addPlayerPokemon(transformation.newSpecies, transformation.previousPokemon.level, newAbilityIndex, undefined);
} }
return pokemonTransformations; return pokemonTransformations;
} }
async function doNewTeamPostProcess(scene: BattleScene, transformations: PokemonTransformation[]) { async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
let atLeastOneNewStarter = false; let atLeastOneNewStarter = false;
for (const transformation of transformations) { for (const transformation of transformations) {
const previousPokemon = transformation.previousPokemon; const previousPokemon = transformation.previousPokemon;
const newPokemon = transformation.newPokemon; const newPokemon = transformation.newPokemon;
const speciesRootForm = newPokemon.species.getRootSpeciesId(); const speciesRootForm = newPokemon.species.getRootSpeciesId();
if (await postProcessTransformedPokemon(scene, previousPokemon, newPokemon, speciesRootForm)) { if (await postProcessTransformedPokemon(previousPokemon, newPokemon, speciesRootForm)) {
atLeastOneNewStarter = true; atLeastOneNewStarter = true;
} }
// 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 scene.addModifier(item, false, false, false, true); await 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)) {
const stats = getOldGateauBoostedStats(newPokemon); const stats = getOldGateauBoostedStats(newPokemon);
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU() const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
.generateType(scene.getPlayerParty(), [ OLD_GATEAU_STATS_UP, stats ]) .generateType(globalScene.getPlayerParty(), [ OLD_GATEAU_STATS_UP, stats ])
?.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 scene.addModifier(modifier, false, false, false, true); await globalScene.addModifier(modifier, false, false, false, true);
} }
} }
@ -418,7 +425,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
} }
// One random pokemon will get its passive unlocked // One random pokemon will get its passive unlocked
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive); const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive);
if (passiveDisabledPokemon?.length > 0) { if (passiveDisabledPokemon?.length > 0) {
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
enablePassiveMon.passive = true; enablePassiveMon.passive = true;
@ -427,27 +434,26 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
// If at least one new starter was unlocked, play 1 fanfare // If at least one new starter was unlocked, play 1 fanfare
if (atLeastOneNewStarter) { if (atLeastOneNewStarter) {
scene.playSound("level_up_fanfare"); globalScene.playSound("level_up_fanfare");
} }
} }
/** /**
* Applies special changes to the newly transformed pokemon, such as passing previous moves, gaining egg moves, etc. * Applies special changes to the newly transformed pokemon, such as passing previous moves, gaining egg moves, etc.
* Returns whether the transformed pokemon unlocks a new starter for the player. * Returns whether the transformed pokemon unlocks a new starter for the player.
* @param scene
* @param previousPokemon * @param previousPokemon
* @param newPokemon * @param newPokemon
* @param speciesRootForm * @param speciesRootForm
* @param forBattle Default `false`. If false, will perform achievements and dex unlocks for the player. * @param forBattle Default `false`. If false, will perform achievements and dex unlocks for the player.
*/ */
async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon: PlayerPokemon, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<boolean> { async function postProcessTransformedPokemon(previousPokemon: PlayerPokemon, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<boolean> {
let isNewStarter = false; let isNewStarter = false;
// Roll HA a second time // Roll HA a second time
if (newPokemon.species.abilityHidden) { if (newPokemon.species.abilityHidden) {
const hiddenIndex = newPokemon.species.ability2 ? 2 : 1; const hiddenIndex = newPokemon.species.ability2 ? 2 : 1;
if (newPokemon.abilityIndex < hiddenIndex) { if (newPokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(256); const hiddenAbilityChance = new IntegerHolder(256);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
@ -469,26 +475,26 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it // For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
if (!forBattle && (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())) { if (!forBattle && (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())) {
if (newPokemon.getSpeciesForm().abilityHidden && newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1) { if (newPokemon.getSpeciesForm().abilityHidden && newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1) {
scene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
if (newPokemon.species.subLegendary) { if (newPokemon.species.subLegendary) {
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
} }
if (newPokemon.species.legendary) { if (newPokemon.species.legendary) {
scene.validateAchv(achvs.CATCH_LEGENDARY); globalScene.validateAchv(achvs.CATCH_LEGENDARY);
} }
if (newPokemon.species.mythical) { if (newPokemon.species.mythical) {
scene.validateAchv(achvs.CATCH_MYTHICAL); globalScene.validateAchv(achvs.CATCH_MYTHICAL);
} }
scene.gameData.updateSpeciesDexIvs(newPokemon.species.getRootSpeciesId(true), newPokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(newPokemon.species.getRootSpeciesId(true), newPokemon.ivs);
const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false); const newStarterUnlocked = await globalScene.gameData.setPokemonCaught(newPokemon, true, false, false);
if (newStarterUnlocked) { if (newStarterUnlocked) {
isNewStarter = true; isNewStarter = true;
await showEncounterText(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() })); await showEncounterText(i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
} }
} }
@ -504,8 +510,8 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
}); });
// For pokemon that the player owns (including ones just caught), gain a candy // For pokemon that the player owns (including ones just caught), gain a candy
if (!forBattle && !!scene.gameData.dexData[speciesRootForm].caughtAttr) { if (!forBattle && !!globalScene.gameData.dexData[speciesRootForm].caughtAttr) {
scene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1); globalScene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
} }
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move and 1 (attempted) STAB move of the new species // Set the moveset of the new pokemon to be the same as previous, but with 1 egg move and 1 (attempted) STAB move of the new species
@ -515,7 +521,7 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
newPokemon.moveset = previousPokemon.moveset.slice(0); newPokemon.moveset = previousPokemon.moveset.slice(0);
const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm, forBattle); const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(newPokemon, speciesRootForm, forBattle);
// Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset) // Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset)
addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex); addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
@ -597,31 +603,31 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num
return newSpecies; return newSpecies;
} }
function doShowDreamBackground(scene: BattleScene) { function doShowDreamBackground() {
const transformationContainer = scene.add.container(0, -scene.game.canvas.height / 6); const transformationContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6);
transformationContainer.name = "Dream Background"; transformationContainer.name = "Dream Background";
// In case it takes a bit for video to load // In case it takes a bit for video to load
const transformationStaticBg = scene.add.rectangle(0, 0, scene.game.canvas.width / 6, scene.game.canvas.height / 6, 0); const transformationStaticBg = globalScene.add.rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0);
transformationStaticBg.setName("Black Background"); transformationStaticBg.setName("Black Background");
transformationStaticBg.setOrigin(0, 0); transformationStaticBg.setOrigin(0, 0);
transformationContainer.add(transformationStaticBg); transformationContainer.add(transformationStaticBg);
transformationStaticBg.setVisible(true); transformationStaticBg.setVisible(true);
const transformationVideoBg: Phaser.GameObjects.Video = scene.add.video(0, 0, "evo_bg").stop(); const transformationVideoBg: Phaser.GameObjects.Video = globalScene.add.video(0, 0, "evo_bg").stop();
transformationVideoBg.setLoop(true); transformationVideoBg.setLoop(true);
transformationVideoBg.setOrigin(0, 0); transformationVideoBg.setOrigin(0, 0);
transformationVideoBg.setScale(0.4359673025); transformationVideoBg.setScale(0.4359673025);
transformationContainer.add(transformationVideoBg); transformationContainer.add(transformationVideoBg);
scene.fieldUI.add(transformationContainer); globalScene.fieldUI.add(transformationContainer);
scene.fieldUI.bringToTop(transformationContainer); globalScene.fieldUI.bringToTop(transformationContainer);
transformationVideoBg.play(); transformationVideoBg.play();
transformationContainer.setVisible(true); transformationContainer.setVisible(true);
transformationContainer.alpha = 0; transformationContainer.alpha = 0;
scene.tweens.add({ globalScene.tweens.add({
targets: transformationContainer, targets: transformationContainer,
alpha: 1, alpha: 1,
duration: 3000, duration: 3000,
@ -629,39 +635,39 @@ function doShowDreamBackground(scene: BattleScene) {
}); });
} }
function doHideDreamBackground(scene: BattleScene) { function doHideDreamBackground() {
const transformationContainer = scene.fieldUI.getByName("Dream Background"); const transformationContainer = globalScene.fieldUI.getByName("Dream Background");
scene.tweens.add({ globalScene.tweens.add({
targets: transformationContainer, targets: transformationContainer,
alpha: 0, alpha: 0,
duration: 3000, duration: 3000,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
onComplete: () => { onComplete: () => {
scene.fieldUI.remove(transformationContainer, true); globalScene.fieldUI.remove(transformationContainer, true);
} }
}); });
} }
function doSideBySideTransformations(scene: BattleScene, transformations: PokemonTransformation[]) { function doSideBySideTransformations(transformations: PokemonTransformation[]) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const allTransformationPromises: Promise<void>[] = []; const allTransformationPromises: Promise<void>[] = [];
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const delay = i * 4000; const delay = i * 4000;
scene.time.delayedCall(delay, () => { globalScene.time.delayedCall(delay, () => {
const transformation = transformations[i]; const transformation = transformations[i];
const pokemon1 = transformation.previousPokemon; const pokemon1 = transformation.previousPokemon;
const pokemon2 = transformation.newPokemon; const pokemon2 = transformation.newPokemon;
const screenPosition = i as TransformationScreenPosition; const screenPosition = i as TransformationScreenPosition;
const transformationPromise = doPokemonTransformationSequence(scene, pokemon1, pokemon2, screenPosition) const transformationPromise = doPokemonTransformationSequence(pokemon1, pokemon2, screenPosition)
.then(() => { .then(() => {
if (transformations.length > i + 3) { if (transformations.length > i + 3) {
const nextTransformationAtPosition = transformations[i + 3]; const nextTransformationAtPosition = transformations[i + 3];
const nextPokemon1 = nextTransformationAtPosition.previousPokemon; const nextPokemon1 = nextTransformationAtPosition.previousPokemon;
const nextPokemon2 = nextTransformationAtPosition.newPokemon; const nextPokemon2 = nextTransformationAtPosition.newPokemon;
allTransformationPromises.push(doPokemonTransformationSequence(scene, nextPokemon1, nextPokemon2, screenPosition)); allTransformationPromises.push(doPokemonTransformationSequence(nextPokemon1, nextPokemon2, screenPosition));
} }
}); });
allTransformationPromises.push(transformationPromise); allTransformationPromises.push(transformationPromise);
@ -682,11 +688,10 @@ function doSideBySideTransformations(scene: BattleScene, transformations: Pokemo
/** /**
* Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`) * Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`)
* @param scene
* @param newPokemon * @param newPokemon
* @param speciesRootForm * @param speciesRootForm
*/ */
async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<number | null> { async function addEggMoveToNewPokemonMoveset(newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<number | null> {
let eggMoveIndex: null | number = null; let eggMoveIndex: null | number = null;
const eggMoves = newPokemon.getEggMoves()?.slice(0); const eggMoves = newPokemon.getEggMoves()?.slice(0);
if (eggMoves) { if (eggMoves) {
@ -712,8 +717,8 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla
} }
// For pokemon that the player owns (including ones just caught), unlock the egg move // For pokemon that the player owns (including ones just caught), unlock the egg move
if (!forBattle && !isNullOrUndefined(randomEggMoveIndex) && !!scene.gameData.dexData[speciesRootForm].caughtAttr) { if (!forBattle && !isNullOrUndefined(randomEggMoveIndex) && !!globalScene.gameData.dexData[speciesRootForm].caughtAttr) {
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true); await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { TextStyle } from "#app/ui/text"; import type { TextStyle } from "#app/ui/text";
export class TextDisplay { export class TextDisplay {
speaker?: string; speaker?: string;

View File

@ -1,15 +1,17 @@
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import type { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
import { Moves } from "#app/enums/moves"; import type { Moves } from "#app/enums/moves";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import BattleScene from "#app/battle-scene"; import type Pokemon from "#app/field/pokemon";
import { Type } from "#enums/type"; import { globalScene } from "#app/global-scene";
import type { Type } from "#enums/type";
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; import type { CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
import { CanLearnMoveRequirement } from "./requirements/can-learn-move-requirement";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>; export type OptionPhaseCallback = () => Promise<void | boolean>;
/** /**
* Used by {@linkcode MysteryEncounterOptionBuilder} class to define required/optional properties on the {@linkcode MysteryEncounterOption} class when building. * Used by {@linkcode MysteryEncounterOptionBuilder} class to define required/optional properties on the {@linkcode MysteryEncounterOption} class when building.
@ -74,21 +76,19 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
/** /**
* Returns true if all {@linkcode EncounterRequirement}s for the option are met * Returns true if all {@linkcode EncounterRequirement}s for the option are met
* @param scene
*/ */
meetsRequirements(scene: BattleScene): boolean { meetsRequirements(): boolean {
return !this.requirements.some(requirement => !requirement.meetsRequirement(scene)) return !this.requirements.some(requirement => !requirement.meetsRequirement())
&& this.meetsSupportingRequirementAndSupportingPokemonSelected(scene) && this.meetsSupportingRequirementAndSupportingPokemonSelected()
&& this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene); && this.meetsPrimaryRequirementAndPrimaryPokemonSelected();
} }
/** /**
* Returns true if all PRIMARY {@linkcode EncounterRequirement}s for the option are met * Returns true if all PRIMARY {@linkcode EncounterRequirement}s for the option are met
* @param scene
* @param pokemon * @param pokemon
*/ */
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean { pokemonMeetsPrimaryRequirements(pokemon: Pokemon): boolean {
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id)); return !this.primaryPokemonRequirements.some(req => !req.queryParty(globalScene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
} }
/** /**
@ -96,16 +96,15 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
* AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}. * AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}.
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined, * If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements. * can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
* @param scene
*/ */
meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean { meetsPrimaryRequirementAndPrimaryPokemonSelected(): boolean {
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) { if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
return true; return true;
} }
let qualified: PlayerPokemon[] = scene.getPlayerParty(); let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
for (const req of this.primaryPokemonRequirements) { for (const req of this.primaryPokemonRequirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
const queryParty = req.queryParty(scene.getPlayerParty()); const queryParty = req.queryParty(globalScene.getPlayerParty());
qualified = qualified.filter(pkmn => queryParty.includes(pkmn)); qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
} else { } else {
this.primaryPokemon = undefined; this.primaryPokemon = undefined;
@ -154,18 +153,17 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
* AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable). * AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable).
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined, * If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements. * can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
* @param scene
*/ */
meetsSupportingRequirementAndSupportingPokemonSelected(scene: BattleScene): boolean { meetsSupportingRequirementAndSupportingPokemonSelected(): boolean {
if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) { if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) {
this.secondaryPokemon = []; this.secondaryPokemon = [];
return true; return true;
} }
let qualified: PlayerPokemon[] = scene.getPlayerParty(); let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
for (const req of this.secondaryPokemonRequirements) { for (const req of this.secondaryPokemonRequirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
const queryParty = req.queryParty(scene.getPlayerParty()); const queryParty = req.queryParty(globalScene.getPlayerParty());
qualified = qualified.filter(pkmn => queryParty.includes(pkmn)); qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
} else { } else {
this.secondaryPokemon = []; this.secondaryPokemon = [];

View File

@ -1,4 +1,4 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { allAbilities } from "#app/data/ability"; import { allAbilities } from "#app/data/ability";
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
@ -6,35 +6,33 @@ import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { AttackTypeBoosterModifier } from "#app/modifier/modifier"; import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { Abilities } from "#enums/abilities"; import type { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
export interface EncounterRequirement { export interface EncounterRequirement {
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met meetsRequirement(): boolean; // Boolean to see if a requirement is met
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string]; getDialogueToken(pokemon?: PlayerPokemon): [string, string];
} }
export abstract class EncounterSceneRequirement implements EncounterRequirement { export abstract class EncounterSceneRequirement implements EncounterRequirement {
/** /**
* Returns whether the EncounterSceneRequirement's... requirements, are met by the given scene * Returns whether the EncounterSceneRequirement's... requirements, are met by the given scene
* @param partyPokemon
*/ */
abstract meetsRequirement(scene: BattleScene): boolean; abstract meetsRequirement(): boolean;
/** /**
* Returns a dialogue token key/value pair for a given Requirement. * Returns a dialogue token key/value pair for a given Requirement.
* Should be overridden by child Requirement classes. * Should be overridden by child Requirement classes.
* @param scene
* @param pokemon * @param pokemon
*/ */
abstract getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string]; abstract getDialogueToken(pokemon?: PlayerPokemon): [string, string];
} }
/** /**
@ -61,33 +59,31 @@ export class CombinationSceneRequirement extends EncounterSceneRequirement {
/** /**
* Checks if all/any requirements are met (depends on {@linkcode isAnd}) * Checks if all/any requirements are met (depends on {@linkcode isAnd})
* @param scene The {@linkcode BattleScene} to check against
* @returns true if all/any requirements are met (depends on {@linkcode isAnd}) * @returns true if all/any requirements are met (depends on {@linkcode isAnd})
*/ */
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
return this.isAnd return this.isAnd
? this.requirements.every(req => req.meetsRequirement(scene)) ? this.requirements.every(req => req.meetsRequirement())
: this.requirements.some(req => req.meetsRequirement(scene)); : this.requirements.some(req => req.meetsRequirement());
} }
/** /**
* Retrieves a dialogue token key/value pair for the given {@linkcode EncounterSceneRequirement | requirements}. * Retrieves a dialogue token key/value pair for the given {@linkcode EncounterSceneRequirement | requirements}.
* @param scene The {@linkcode BattleScene} to check against
* @param pokemon The {@linkcode PlayerPokemon} to check against * @param pokemon The {@linkcode PlayerPokemon} to check against
* @returns A dialogue token key/value pair * @returns A dialogue token key/value pair
* @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported) * @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported)
*/ */
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
if (this.isAnd) { if (this.isAnd) {
throw new Error("Not implemented (Sorry)"); throw new Error("Not implemented (Sorry)");
} else { } else {
for (const req of this.requirements) { for (const req of this.requirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
return req.getDialogueToken(scene, pokemon); return req.getDialogueToken(pokemon);
} }
} }
return this.requirements[0].getDialogueToken(scene, pokemon); return this.requirements[0].getDialogueToken(pokemon);
} }
} }
} }
@ -98,9 +94,8 @@ export abstract class EncounterPokemonRequirement implements EncounterRequiremen
/** /**
* Returns whether the EncounterPokemonRequirement's... requirements, are met by the given scene * Returns whether the EncounterPokemonRequirement's... requirements, are met by the given scene
* @param partyPokemon
*/ */
abstract meetsRequirement(scene: BattleScene): boolean; abstract meetsRequirement(): boolean;
/** /**
* Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned. * Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned.
@ -111,10 +106,9 @@ export abstract class EncounterPokemonRequirement implements EncounterRequiremen
/** /**
* Returns a dialogue token key/value pair for a given Requirement. * Returns a dialogue token key/value pair for a given Requirement.
* Should be overridden by child Requirement classes. * Should be overridden by child Requirement classes.
* @param scene
* @param pokemon * @param pokemon
*/ */
abstract getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string]; abstract getDialogueToken(pokemon?: PlayerPokemon): [string, string];
} }
/** /**
@ -143,13 +137,12 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
/** /**
* Checks if all/any requirements are met (depends on {@linkcode isAnd}) * Checks if all/any requirements are met (depends on {@linkcode isAnd})
* @param scene The {@linkcode BattleScene} to check against
* @returns true if all/any requirements are met (depends on {@linkcode isAnd}) * @returns true if all/any requirements are met (depends on {@linkcode isAnd})
*/ */
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
return this.isAnd return this.isAnd
? this.requirements.every(req => req.meetsRequirement(scene)) ? this.requirements.every(req => req.meetsRequirement())
: this.requirements.some(req => req.meetsRequirement(scene)); : this.requirements.some(req => req.meetsRequirement());
} }
/** /**
@ -168,22 +161,21 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
/** /**
* Retrieves a dialogue token key/value pair for the given {@linkcode EncounterPokemonRequirement | requirements}. * Retrieves a dialogue token key/value pair for the given {@linkcode EncounterPokemonRequirement | requirements}.
* @param scene The {@linkcode BattleScene} to check against
* @param pokemon The {@linkcode PlayerPokemon} to check against * @param pokemon The {@linkcode PlayerPokemon} to check against
* @returns A dialogue token key/value pair * @returns A dialogue token key/value pair
* @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported) * @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported)
*/ */
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
if (this.isAnd) { if (this.isAnd) {
throw new Error("Not implemented (Sorry)"); throw new Error("Not implemented (Sorry)");
} else { } else {
for (const req of this.requirements) { for (const req of this.requirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
return req.getDialogueToken(scene, pokemon); return req.getDialogueToken(pokemon);
} }
} }
return this.requirements[0].getDialogueToken(scene, pokemon); return this.requirements[0].getDialogueToken(pokemon);
} }
} }
} }
@ -200,12 +192,12 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
this.previousEncounterRequirement = previousEncounterRequirement; this.previousEncounterRequirement = previousEncounterRequirement;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
return scene.mysteryEncounterSaveData.encounteredEvents.some(e => e.type === this.previousEncounterRequirement); return globalScene.mysteryEncounterSaveData.encounteredEvents.some(e => e.type === this.previousEncounterRequirement);
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "previousEncounter", scene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? "" ]; return [ "previousEncounter", globalScene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? "" ];
} }
} }
@ -222,9 +214,9 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
this.waveRange = waveRange; this.waveRange = waveRange;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) { if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
const waveIndex = scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
if (waveIndex >= 0 && (this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)) { if (waveIndex >= 0 && (this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)) {
return false; return false;
} }
@ -232,8 +224,8 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
return true; return true;
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ]; return [ "waveIndex", globalScene.currentBattle.waveIndex.toString() ];
} }
} }
@ -257,12 +249,12 @@ export class WaveModulusRequirement extends EncounterSceneRequirement {
this.modulusValue = modulusValue; this.modulusValue = modulusValue;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
return this.waveModuli.includes(scene.currentBattle.waveIndex % this.modulusValue); return this.waveModuli.includes(globalScene.currentBattle.waveIndex % this.modulusValue);
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ]; return [ "waveIndex", globalScene.currentBattle.waveIndex.toString() ];
} }
} }
@ -274,8 +266,8 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [ timeOfDay ]; this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [ timeOfDay ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const timeOfDay = scene.arena?.getTimeOfDay(); const timeOfDay = globalScene.arena?.getTimeOfDay();
if (!isNullOrUndefined(timeOfDay) && this.requiredTimeOfDay?.length > 0 && !this.requiredTimeOfDay.includes(timeOfDay)) { if (!isNullOrUndefined(timeOfDay) && this.requiredTimeOfDay?.length > 0 && !this.requiredTimeOfDay.includes(timeOfDay)) {
return false; return false;
} }
@ -283,8 +275,8 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
return true; return true;
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "timeOfDay", TimeOfDay[scene.arena.getTimeOfDay()].toLocaleLowerCase() ]; return [ "timeOfDay", TimeOfDay[globalScene.arena.getTimeOfDay()].toLocaleLowerCase() ];
} }
} }
@ -296,8 +288,8 @@ export class WeatherRequirement extends EncounterSceneRequirement {
this.requiredWeather = Array.isArray(weather) ? weather : [ weather ]; this.requiredWeather = Array.isArray(weather) ? weather : [ weather ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const currentWeather = scene.arena.weather?.weatherType; const currentWeather = globalScene.arena.weather?.weatherType;
if (!isNullOrUndefined(currentWeather) && this.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!)) { if (!isNullOrUndefined(currentWeather) && this.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!)) {
return false; return false;
} }
@ -305,8 +297,8 @@ export class WeatherRequirement extends EncounterSceneRequirement {
return true; return true;
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const currentWeather = scene.arena.weather?.weatherType; const currentWeather = globalScene.arena.weather?.weatherType;
let token = ""; let token = "";
if (!isNullOrUndefined(currentWeather)) { if (!isNullOrUndefined(currentWeather)) {
token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase(); token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase();
@ -331,9 +323,9 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
this.excludeDisallowedPokemon = excludeDisallowedPokemon; this.excludeDisallowedPokemon = excludeDisallowedPokemon;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) { if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) {
const partySize = this.excludeDisallowedPokemon ? scene.getPokemonAllowedInBattle().length : scene.getPlayerParty().length; const partySize = this.excludeDisallowedPokemon ? globalScene.getPokemonAllowedInBattle().length : globalScene.getPlayerParty().length;
if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) { if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) {
return false; return false;
} }
@ -342,8 +334,8 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
return true; return true;
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "partySize", scene.getPlayerParty().length.toString() ]; return [ "partySize", globalScene.getPlayerParty().length.toString() ];
} }
} }
@ -357,14 +349,14 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [ heldItem ]; this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [ heldItem ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) {
return false; return false;
} }
let modifierCount = 0; let modifierCount = 0;
this.requiredHeldItemModifiers.forEach(modifier => { this.requiredHeldItemModifiers.forEach(modifier => {
const matchingMods = scene.findModifiers(m => m.constructor.name === modifier); const matchingMods = globalScene.findModifiers(m => m.constructor.name === modifier);
if (matchingMods?.length > 0) { if (matchingMods?.length > 0) {
matchingMods.forEach(matchingMod => { matchingMods.forEach(matchingMod => {
modifierCount += matchingMod.stackCount; modifierCount += matchingMod.stackCount;
@ -375,7 +367,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
return modifierCount >= this.minNumberOfItems; return modifierCount >= this.minNumberOfItems;
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "requiredItem", this.requiredHeldItemModifiers[0] ]; return [ "requiredItem", this.requiredHeldItemModifiers[0] ];
} }
} }
@ -390,20 +382,20 @@ export class MoneyRequirement extends EncounterSceneRequirement {
this.scalingMultiplier = scalingMultiplier ?? 0; this.scalingMultiplier = scalingMultiplier ?? 0;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const money = scene.money; const money = globalScene.money;
if (isNullOrUndefined(money)) { if (isNullOrUndefined(money)) {
return false; return false;
} }
if (this.scalingMultiplier > 0) { if (this.scalingMultiplier > 0) {
this.requiredMoney = scene.getWaveMoneyAmount(this.scalingMultiplier); this.requiredMoney = globalScene.getWaveMoneyAmount(this.scalingMultiplier);
} }
return !(this.requiredMoney > 0 && this.requiredMoney > money); return !(this.requiredMoney > 0 && this.requiredMoney > money);
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const value = this.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString(); const value = this.scalingMultiplier > 0 ? globalScene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
return [ "money", value ]; return [ "money", value ];
} }
} }
@ -420,8 +412,8 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
this.requiredSpecies = Array.isArray(species) ? species : [ species ]; this.requiredSpecies = Array.isArray(species) ? species : [ species ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) {
return false; return false;
} }
@ -437,7 +429,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) { if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
return [ "species", Species[pokemon.species.speciesId] ]; return [ "species", Species[pokemon.species.speciesId] ];
} }
@ -458,8 +450,8 @@ export class NatureRequirement extends EncounterPokemonRequirement {
this.requiredNature = Array.isArray(nature) ? nature : [ nature ]; this.requiredNature = Array.isArray(nature) ? nature : [ nature ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) {
return false; return false;
} }
@ -475,7 +467,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) { if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) {
return [ "nature", Nature[pokemon.nature] ]; return [ "nature", Nature[pokemon.nature] ];
} }
@ -497,8 +489,8 @@ export class TypeRequirement extends EncounterPokemonRequirement {
this.requiredType = Array.isArray(type) ? type : [ type ]; this.requiredType = Array.isArray(type) ? type : [ type ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
let partyPokemon = scene.getPlayerParty(); let partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon)) { if (isNullOrUndefined(partyPokemon)) {
return false; return false;
@ -520,7 +512,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty)); const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty));
if (includedTypes.length > 0) { if (includedTypes.length > 0) {
return [ "type", Type[includedTypes[0]] ]; return [ "type", Type[includedTypes[0]] ];
@ -544,8 +536,8 @@ export class MoveRequirement extends EncounterPokemonRequirement {
this.requiredMoves = Array.isArray(moves) ? moves : [ moves ]; this.requiredMoves = Array.isArray(moves) ? moves : [ moves ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
return false; return false;
} }
@ -566,7 +558,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId)); const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId));
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) { if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
return [ "move", includedMoves[0].getName() ]; return [ "move", includedMoves[0].getName() ];
@ -593,8 +585,8 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [ learnableMove ]; this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [ learnableMove ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
return false; return false;
} }
@ -610,7 +602,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove)); const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove));
if (includedCompatMoves.length > 0) { if (includedCompatMoves.length > 0) {
return [ "compatibleMove", Moves[includedCompatMoves[0]] ]; return [ "compatibleMove", Moves[includedCompatMoves[0]] ];
@ -634,8 +626,8 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
this.requiredAbilities = Array.isArray(abilities) ? abilities : [ abilities ]; this.requiredAbilities = Array.isArray(abilities) ? abilities : [ abilities ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) {
return false; return false;
} }
@ -655,7 +647,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(_scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const matchingAbility = this.requiredAbilities.find(a => pokemon?.hasAbility(a, false)); const matchingAbility = this.requiredAbilities.find(a => pokemon?.hasAbility(a, false));
if (!isNullOrUndefined(matchingAbility)) { if (!isNullOrUndefined(matchingAbility)) {
return [ "ability", allAbilities[matchingAbility].name ]; return [ "ability", allAbilities[matchingAbility].name ];
@ -676,8 +668,8 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [ statusEffect ]; this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [ statusEffect ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) {
return false; return false;
} }
@ -713,7 +705,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const reqStatus = this.requiredStatusEffect.filter((a) => { const reqStatus = this.requiredStatusEffect.filter((a) => {
if (a === StatusEffect.NONE) { if (a === StatusEffect.NONE) {
return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === a; return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === a;
@ -745,8 +737,8 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [ formChangeItem ]; this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [ formChangeItem ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) {
return false; return false;
} }
@ -775,7 +767,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem)); const requiredItems = this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem));
if (requiredItems.length > 0) { if (requiredItems.length > 0) {
return [ "formChangeItem", FormChangeItem[requiredItems[0]] ]; return [ "formChangeItem", FormChangeItem[requiredItems[0]] ];
@ -797,8 +789,8 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [ evolutionItems ]; this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [ evolutionItems ];
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
return false; return false;
} }
@ -825,7 +817,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredEvolutionItem.filter((evoItem) => this.filterByEvo(pokemon, evoItem)); const requiredItems = this.requiredEvolutionItem.filter((evoItem) => this.filterByEvo(pokemon, evoItem));
if (requiredItems.length > 0) { if (requiredItems.length > 0) {
return [ "evolutionItem", EvolutionItem[requiredItems[0]] ]; return [ "evolutionItem", EvolutionItem[requiredItems[0]] ];
@ -848,8 +840,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
this.requireTransferable = requireTransferable; this.requireTransferable = requireTransferable;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon)) { if (isNullOrUndefined(partyPokemon)) {
return false; return false;
} }
@ -873,7 +865,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter((it) => { const requiredItems = pokemon?.getHeldItems().filter((it) => {
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem) return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
&& (!this.requireTransferable || it.isTransferable); && (!this.requireTransferable || it.isTransferable);
@ -899,8 +891,8 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
this.requireTransferable = requireTransferable; this.requireTransferable = requireTransferable;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
if (isNullOrUndefined(partyPokemon)) { if (isNullOrUndefined(partyPokemon)) {
return false; return false;
} }
@ -928,7 +920,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter((it) => { const requiredItems = pokemon?.getHeldItems().filter((it) => {
return this.requiredHeldItemTypes.some(heldItemType => return this.requiredHeldItemTypes.some(heldItemType =>
it instanceof AttackTypeBoosterModifier it instanceof AttackTypeBoosterModifier
@ -954,10 +946,10 @@ export class LevelRequirement extends EncounterPokemonRequirement {
this.requiredLevelRange = requiredLevelRange; this.requiredLevelRange = requiredLevelRange;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
// Party Pokemon inside required level range // Party Pokemon inside required level range
if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) { if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon); const pokemonInRange = this.queryParty(partyPokemon);
if (pokemonInRange.length < this.minNumberOfPokemon) { if (pokemonInRange.length < this.minNumberOfPokemon) {
return false; return false;
@ -975,7 +967,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "level", pokemon?.level.toString() ?? "" ]; return [ "level", pokemon?.level.toString() ?? "" ];
} }
} }
@ -992,10 +984,10 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
this.requiredFriendshipRange = requiredFriendshipRange; this.requiredFriendshipRange = requiredFriendshipRange;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
// Party Pokemon inside required friendship range // Party Pokemon inside required friendship range
if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) { if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon); const pokemonInRange = this.queryParty(partyPokemon);
if (pokemonInRange.length < this.minNumberOfPokemon) { if (pokemonInRange.length < this.minNumberOfPokemon) {
return false; return false;
@ -1013,7 +1005,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "friendship", pokemon?.friendship.toString() ?? "" ]; return [ "friendship", pokemon?.friendship.toString() ?? "" ];
} }
} }
@ -1035,10 +1027,10 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
this.requiredHealthRange = requiredHealthRange; this.requiredHealthRange = requiredHealthRange;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
// Party Pokemon's health inside required health range // Party Pokemon's health inside required health range
if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) { if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon); const pokemonInRange = this.queryParty(partyPokemon);
if (pokemonInRange.length < this.minNumberOfPokemon) { if (pokemonInRange.length < this.minNumberOfPokemon) {
return false; return false;
@ -1058,7 +1050,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const hpRatio = pokemon?.getHpRatio(); const hpRatio = pokemon?.getHpRatio();
if (!isNullOrUndefined(hpRatio)) { if (!isNullOrUndefined(hpRatio)) {
return [ "healthRatio", Math.floor(hpRatio * 100).toString() + "%" ]; return [ "healthRatio", Math.floor(hpRatio * 100).toString() + "%" ];
@ -1079,10 +1071,10 @@ export class WeightRequirement extends EncounterPokemonRequirement {
this.requiredWeightRange = requiredWeightRange; this.requiredWeightRange = requiredWeightRange;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
// Party Pokemon's weight inside required weight range // Party Pokemon's weight inside required weight range
if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) { if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) {
const partyPokemon = scene.getPlayerParty(); const partyPokemon = globalScene.getPlayerParty();
const pokemonInRange = this.queryParty(partyPokemon); const pokemonInRange = this.queryParty(partyPokemon);
if (pokemonInRange.length < this.minNumberOfPokemon) { if (pokemonInRange.length < this.minNumberOfPokemon) {
return false; return false;
@ -1100,7 +1092,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
return [ "weight", pokemon?.getWeight().toString() ?? "" ]; return [ "weight", pokemon?.getWeight().toString() ?? "" ];
} }
} }

View File

@ -1,7 +1,7 @@
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters"; import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
export class SeenEncounterData { export class SeenEncounterData {
type: MysteryEncounterType; type: MysteryEncounterType;

View File

@ -1,21 +1,26 @@
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils"; import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro"; import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { StatusEffect } from "#enums/status-effect"; import type { StatusEffect } from "#enums/status-effect";
import MysteryEncounterDialogue, { OptionTextDisplay } from "./mystery-encounter-dialogue"; import type { OptionTextDisplay } from "./mystery-encounter-dialogue";
import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option"; import type MysteryEncounterDialogue from "./mystery-encounter-dialogue";
import type { OptionPhaseCallback } from "./mystery-encounter-option";
import type MysteryEncounterOption from "./mystery-encounter-option";
import { MysteryEncounterOptionBuilder } from "./mystery-encounter-option";
import { EncounterPokemonRequirement, EncounterSceneRequirement, HealthRatioRequirement, PartySizeRequirement, StatusEffectRequirement, WaveRangeRequirement } from "./mystery-encounter-requirements"; import { EncounterPokemonRequirement, EncounterSceneRequirement, HealthRatioRequirement, PartySizeRequirement, StatusEffectRequirement, WaveRangeRequirement } from "./mystery-encounter-requirements";
import { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { GameModes } from "#app/game-mode"; import type { GameModes } from "#app/game-mode";
import { EncounterAnim } from "#enums/encounter-anims"; import type { EncounterAnim } from "#enums/encounter-anims";
import { Challenges } from "#enums/challenges"; import type { Challenges } from "#enums/challenges";
import { globalScene } from "#app/global-scene";
export interface EncounterStartOfBattleEffect { export interface EncounterStartOfBattleEffect {
sourcePokemon?: Pokemon; sourcePokemon?: Pokemon;
@ -55,11 +60,11 @@ export interface IMysteryEncounter {
skipToFightInput: boolean; skipToFightInput: boolean;
preventGameStatsUpdates: boolean; preventGameStatsUpdates: boolean;
onInit?: (scene: BattleScene) => boolean; onInit?: () => boolean;
onVisualsStart?: (scene: BattleScene) => boolean; onVisualsStart?: () => boolean;
doEncounterExp?: (scene: BattleScene) => boolean; doEncounterExp?: () => boolean;
doEncounterRewards?: (scene: BattleScene) => boolean; doEncounterRewards?: () => boolean;
doContinueEncounter?: (scene: BattleScene) => Promise<void>; doContinueEncounter?: () => Promise<void>;
requirements: EncounterSceneRequirement[]; requirements: EncounterSceneRequirement[];
primaryPokemonRequirements: EncounterPokemonRequirement[]; primaryPokemonRequirements: EncounterPokemonRequirement[];
@ -159,24 +164,24 @@ export default class MysteryEncounter implements IMysteryEncounter {
// #region Event callback functions // #region Event callback functions
/** Event when Encounter is first loaded, use it for data conditioning */ /** Event when Encounter is first loaded, use it for data conditioning */
onInit?: (scene: BattleScene) => boolean; onInit?: () => boolean;
/** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */ /** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */
onVisualsStart?: (scene: BattleScene) => boolean; onVisualsStart?: () => boolean;
/** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */ /** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */
onTurnStart?: (scene: BattleScene) => boolean; onTurnStart?: () => boolean;
/** Event prior to any rewards logic in {@linkcode MysteryEncounterRewardsPhase} */ /** Event prior to any rewards logic in {@linkcode MysteryEncounterRewardsPhase} */
onRewards?: (scene: BattleScene) => Promise<void>; onRewards?: () => Promise<void>;
/** Will provide the player party EXP before rewards are displayed for that wave */ /** Will provide the player party EXP before rewards are displayed for that wave */
doEncounterExp?: (scene: BattleScene) => boolean; doEncounterExp?: () => boolean;
/** Will provide the player a rewards shop for that wave */ /** Will provide the player a rewards shop for that wave */
doEncounterRewards?: (scene: BattleScene) => boolean; doEncounterRewards?: () => boolean;
/** Will execute callback during VictoryPhase of a continuousEncounter */ /** Will execute callback during VictoryPhase of a continuousEncounter */
doContinueEncounter?: (scene: BattleScene) => Promise<void>; doContinueEncounter?: () => Promise<void>;
/** /**
* Can perform special logic when a ME battle is lost, before GameOver/battle retry prompt. * Can perform special logic when a ME battle is lost, before GameOver/battle retry prompt.
* Should return `true` if it is treated as "real" Game Over, `false` if not. * Should return `true` if it is treated as "real" Game Over, `false` if not.
*/ */
onGameOver?: (scene: BattleScene) => boolean; onGameOver?: () => boolean;
/** /**
* Requirements * Requirements
@ -296,13 +301,12 @@ export default class MysteryEncounter implements IMysteryEncounter {
/** /**
* Checks if the current scene state meets the requirements for the {@linkcode MysteryEncounter} to spawn * Checks if the current scene state meets the requirements for the {@linkcode MysteryEncounter} to spawn
* This is used to filter the pool of encounters down to only the ones with all requirements met * This is used to filter the pool of encounters down to only the ones with all requirements met
* @param scene
* @returns * @returns
*/ */
meetsRequirements(scene: BattleScene): boolean { meetsRequirements(): boolean {
const sceneReq = !this.requirements.some(requirement => !requirement.meetsRequirement(scene)); const sceneReq = !this.requirements.some(requirement => !requirement.meetsRequirement());
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(); // secondary is checked first to handle cases of primary overlapping with secondary
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene); const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected();
return sceneReq && secReqs && priReqs; return sceneReq && secReqs && priReqs;
} }
@ -310,11 +314,10 @@ export default class MysteryEncounter implements IMysteryEncounter {
/** /**
* Checks if a specific player pokemon meets all given primary EncounterPokemonRequirements * Checks if a specific player pokemon meets all given primary EncounterPokemonRequirements
* Used automatically as part of {@linkcode meetsRequirements}, but can also be used to manually check certain Pokemon where needed * Used automatically as part of {@linkcode meetsRequirements}, but can also be used to manually check certain Pokemon where needed
* @param scene
* @param pokemon * @param pokemon
*/ */
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean { pokemonMeetsPrimaryRequirements(pokemon: Pokemon): boolean {
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id)); return !this.primaryPokemonRequirements.some(req => !req.queryParty(globalScene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
} }
/** /**
@ -322,22 +325,21 @@ export default class MysteryEncounter implements IMysteryEncounter {
* AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}. * AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}.
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined, * If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements. * can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
* @param scene
*/ */
private meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean { private meetsPrimaryRequirementAndPrimaryPokemonSelected(): boolean {
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) { if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
const activeMon = scene.getPlayerParty().filter(p => p.isActive(true)); const activeMon = globalScene.getPlayerParty().filter(p => p.isActive(true));
if (activeMon.length > 0) { if (activeMon.length > 0) {
this.primaryPokemon = activeMon[0]; this.primaryPokemon = activeMon[0];
} else { } else {
this.primaryPokemon = scene.getPlayerParty().filter(p => p.isAllowedInBattle())[0]; this.primaryPokemon = globalScene.getPlayerParty().filter(p => p.isAllowedInBattle())[0];
} }
return true; return true;
} }
let qualified: PlayerPokemon[] = scene.getPlayerParty(); let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
for (const req of this.primaryPokemonRequirements) { for (const req of this.primaryPokemonRequirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn)); qualified = qualified.filter(pkmn => req.queryParty(globalScene.getPlayerParty()).includes(pkmn));
} else { } else {
this.primaryPokemon = undefined; this.primaryPokemon = undefined;
return false; return false;
@ -386,18 +388,17 @@ export default class MysteryEncounter implements IMysteryEncounter {
* AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable). * AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable).
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined, * If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements. * can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
* @param scene
*/ */
private meetsSecondaryRequirementAndSecondaryPokemonSelected(scene: BattleScene): boolean { private meetsSecondaryRequirementAndSecondaryPokemonSelected(): boolean {
if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) { if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) {
this.secondaryPokemon = []; this.secondaryPokemon = [];
return true; return true;
} }
let qualified: PlayerPokemon[] = scene.getPlayerParty(); let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
for (const req of this.secondaryPokemonRequirements) { for (const req of this.secondaryPokemonRequirements) {
if (req.meetsRequirement(scene)) { if (req.meetsRequirement()) {
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn)); qualified = qualified.filter(pkmn => req.queryParty(globalScene.getPlayerParty()).includes(pkmn));
} else { } else {
this.secondaryPokemon = []; this.secondaryPokemon = [];
return false; return false;
@ -409,10 +410,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
/** /**
* Initializes encounter intro sprites based on the sprite configs defined in spriteConfigs * Initializes encounter intro sprites based on the sprite configs defined in spriteConfigs
* @param scene
*/ */
initIntroVisuals(scene: BattleScene): void { initIntroVisuals(): void {
this.introVisuals = new MysteryEncounterIntroVisuals(scene, this); this.introVisuals = new MysteryEncounterIntroVisuals(this);
} }
/** /**
@ -420,11 +420,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
* Will use the first support pokemon in list * Will use the first support pokemon in list
* For multiple support pokemon in the dialogue token, it will have to be overridden. * For multiple support pokemon in the dialogue token, it will have to be overridden.
*/ */
populateDialogueTokensFromRequirements(scene: BattleScene): void { populateDialogueTokensFromRequirements(): void {
this.meetsRequirements(scene); this.meetsRequirements();
if (this.requirements?.length > 0) { if (this.requirements?.length > 0) {
for (const req of this.requirements) { for (const req of this.requirements) {
const dialogueToken = req.getDialogueToken(scene); const dialogueToken = req.getDialogueToken();
if (dialogueToken?.length === 2) { if (dialogueToken?.length === 2) {
this.setDialogueToken(...dialogueToken); this.setDialogueToken(...dialogueToken);
} }
@ -434,7 +434,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
this.setDialogueToken("primaryName", this.primaryPokemon.getNameToRender()); this.setDialogueToken("primaryName", this.primaryPokemon.getNameToRender());
for (const req of this.primaryPokemonRequirements) { for (const req of this.primaryPokemonRequirements) {
if (!req.invertQuery) { if (!req.invertQuery) {
const value = req.getDialogueToken(scene, this.primaryPokemon); const value = req.getDialogueToken(this.primaryPokemon);
if (value?.length === 2) { if (value?.length === 2) {
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]); this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
} }
@ -445,7 +445,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
this.setDialogueToken("secondaryName", this.secondaryPokemon[0].getNameToRender()); this.setDialogueToken("secondaryName", this.secondaryPokemon[0].getNameToRender());
for (const req of this.secondaryPokemonRequirements) { for (const req of this.secondaryPokemonRequirements) {
if (!req.invertQuery) { if (!req.invertQuery) {
const value = req.getDialogueToken(scene, this.secondaryPokemon[0]); const value = req.getDialogueToken(this.secondaryPokemon[0]);
if (value?.length === 2) { if (value?.length === 2) {
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]); this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
} }
@ -457,11 +457,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
// Dialogue tokens for options // Dialogue tokens for options
for (let i = 0; i < this.options.length; i++) { for (let i = 0; i < this.options.length; i++) {
const opt = this.options[i]; const opt = this.options[i];
opt.meetsRequirements(scene); opt.meetsRequirements();
const j = i + 1; const j = i + 1;
if (opt.requirements.length > 0) { if (opt.requirements.length > 0) {
for (const req of opt.requirements) { for (const req of opt.requirements) {
const dialogueToken = req.getDialogueToken(scene); const dialogueToken = req.getDialogueToken();
if (dialogueToken?.length === 2) { if (dialogueToken?.length === 2) {
this.setDialogueToken("option" + j + capitalizeFirstLetter(dialogueToken[0]), dialogueToken[1]); this.setDialogueToken("option" + j + capitalizeFirstLetter(dialogueToken[0]), dialogueToken[1]);
} }
@ -471,7 +471,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.getNameToRender()); this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.getNameToRender());
for (const req of opt.primaryPokemonRequirements) { for (const req of opt.primaryPokemonRequirements) {
if (!req.invertQuery) { if (!req.invertQuery) {
const value = req.getDialogueToken(scene, opt.primaryPokemon); const value = req.getDialogueToken(opt.primaryPokemon);
if (value?.length === 2) { if (value?.length === 2) {
this.setDialogueToken("option" + j + "Primary" + capitalizeFirstLetter(value[0]), value[1]); this.setDialogueToken("option" + j + "Primary" + capitalizeFirstLetter(value[0]), value[1]);
} }
@ -482,7 +482,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
this.setDialogueToken("option" + j + "SecondaryName", opt.secondaryPokemon[0].getNameToRender()); this.setDialogueToken("option" + j + "SecondaryName", opt.secondaryPokemon[0].getNameToRender());
for (const req of opt.secondaryPokemonRequirements) { for (const req of opt.secondaryPokemonRequirements) {
if (!req.invertQuery) { if (!req.invertQuery) {
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]); const value = req.getDialogueToken(opt.secondaryPokemon[0]);
if (value?.length === 2) { if (value?.length === 2) {
this.setDialogueToken("option" + j + "Secondary" + capitalizeFirstLetter(value[0]), value[1]); this.setDialogueToken("option" + j + "Secondary" + capitalizeFirstLetter(value[0]), value[1]);
} }
@ -518,10 +518,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
/** /**
* Maintains seed offset for RNG consistency * Maintains seed offset for RNG consistency
* Increments if the same {@linkcode MysteryEncounter} has multiple option select cycles * Increments if the same {@linkcode MysteryEncounter} has multiple option select cycles
* @param scene
*/ */
updateSeedOffset(scene: BattleScene) { updateSeedOffset() {
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000; const currentOffset = this.seedOffset ?? globalScene.currentBattle.waveIndex * 1000;
this.seedOffset = currentOffset + 512; this.seedOffset = currentOffset + 512;
} }
} }
@ -858,7 +857,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter * @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter
* @returns * @returns
*/ */
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> { withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
return Object.assign(this, { doEncounterRewards: doEncounterRewards }); return Object.assign(this, { doEncounterRewards: doEncounterRewards });
} }
@ -872,7 +871,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter * @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter
* @returns * @returns
*/ */
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> { withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
return Object.assign(this, { doEncounterExp: doEncounterExp }); return Object.assign(this, { doEncounterExp: doEncounterExp });
} }
@ -883,7 +882,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param onInit Synchronous callback function to perform as soon as the encounter is selected for the next phase * @param onInit Synchronous callback function to perform as soon as the encounter is selected for the next phase
* @returns * @returns
*/ */
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> { withOnInit(onInit: () => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
return Object.assign(this, { onInit }); return Object.assign(this, { onInit });
} }
@ -893,7 +892,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param onVisualsStart Synchronous callback function to perform as soon as the enemy field finishes sliding in * @param onVisualsStart Synchronous callback function to perform as soon as the enemy field finishes sliding in
* @returns * @returns
*/ */
withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> { withOnVisualsStart(onVisualsStart: () => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
return Object.assign(this, { onVisualsStart: onVisualsStart }); return Object.assign(this, { onVisualsStart: onVisualsStart });
} }

View File

@ -10,7 +10,7 @@ import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounte
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter"; import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter";
import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
import MysteryEncounter from "./mystery-encounter"; import type MysteryEncounter from "./mystery-encounter";
import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";

View File

@ -1,8 +1,9 @@
import BattleScene from "#app/battle-scene"; import type { Moves } from "#app/enums/moves";
import { Moves } from "#app/enums/moves"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { globalScene } from "#app/global-scene";
/** /**
* {@linkcode CanLearnMoveRequirement} options * {@linkcode CanLearnMoveRequirement} options
@ -38,8 +39,8 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
this.invertQuery = options.invertQuery ?? false; this.invertQuery = options.invertQuery ?? false;
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(): boolean {
const partyPokemon = scene.getPlayerParty().filter((pkm) => (this.includeFainted ? pkm.isAllowedInChallenge() : pkm.isAllowedInBattle())); const partyPokemon = globalScene.getPlayerParty().filter((pkm) => (this.includeFainted ? pkm.isAllowedInChallenge() : pkm.isAllowedInBattle()));
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) { if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
return false; return false;
@ -63,7 +64,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
} }
} }
override getDialogueToken(_scene: BattleScene, _pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(__pokemon?: PlayerPokemon): [string, string] {
return [ "requiredMoves", this.requiredMoves.map(m => new PokemonMove(m).getName()).join(", ") ]; return [ "requiredMoves", this.requiredMoves.map(m => new PokemonMove(m).getName()).join(", ") ];
} }

View File

@ -1,23 +1,24 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { getTextWithColors, TextStyle } from "#app/ui/text"; import type { TextStyle } from "#app/ui/text";
import { getTextWithColors } from "#app/ui/text";
import { UiTheme } from "#enums/ui-theme"; import { UiTheme } from "#enums/ui-theme";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
/** /**
* Will inject all relevant dialogue tokens that exist in the {@linkcode BattleScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. * Will inject all relevant dialogue tokens that exist in the {@linkcode BattlegScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text.
* Also adds BBCodeText fragments for colored text, if applicable * Also adds BBCodeText fragments for colored text, if applicable
* @param keyOrString * @param keyOrString
* @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly * @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly
*/ */
export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle): string | null { export function getEncounterText(keyOrString?: string, primaryStyle?: TextStyle): string | null {
if (isNullOrUndefined(keyOrString)) { if (isNullOrUndefined(keyOrString)) {
return null; return null;
} }
const uiTheme = scene.uiTheme ?? UiTheme.DEFAULT; const uiTheme = globalScene.uiTheme ?? UiTheme.DEFAULT;
let textString: string | null = getTextWithDialogueTokens(scene, keyOrString); let textString: string | null = getTextWithDialogueTokens(keyOrString);
// Can only color the text if a Primary Style is defined // Can only color the text if a Primary Style is defined
// primaryStyle is applied to all text that does not have its own specified style // primaryStyle is applied to all text that does not have its own specified style
@ -29,12 +30,12 @@ export function getEncounterText(scene: BattleScene, keyOrString?: string, prima
} }
/** /**
* Helper function to inject {@linkcode BattleScene.currentBattle.mysteryEncounter.dialogueTokens} into a given content string * Helper function to inject {@linkcode globalScene.currentBattle.mysteryEncounter.dialogueTokens} into a given content string
* @param scene * @param scene
* @param keyOrString * @param keyOrString
*/ */
function getTextWithDialogueTokens(scene: BattleScene, keyOrString: string): string | null { function getTextWithDialogueTokens(keyOrString: string): string | null {
const tokens = scene.currentBattle?.mysteryEncounter?.dialogueTokens; const tokens = globalScene.currentBattle?.mysteryEncounter?.dialogueTokens;
if (i18next.exists(keyOrString, tokens)) { if (i18next.exists(keyOrString, tokens)) {
return i18next.t(keyOrString, tokens) as string; return i18next.t(keyOrString, tokens) as string;
@ -48,9 +49,9 @@ function getTextWithDialogueTokens(scene: BattleScene, keyOrString: string): str
* @param scene * @param scene
* @param contentKey * @param contentKey
*/ */
export function queueEncounterMessage(scene: BattleScene, contentKey: string): void { export function queueEncounterMessage(contentKey: string): void {
const text: string | null = getEncounterText(scene, contentKey); const text: string | null = getEncounterText(contentKey);
scene.queueMessage(text ?? "", null, true); globalScene.queueMessage(text ?? "", null, true);
} }
/** /**
@ -62,10 +63,10 @@ export function queueEncounterMessage(scene: BattleScene, contentKey: string): v
* @param callbackDelay * @param callbackDelay
* @param promptDelay * @param promptDelay
*/ */
export function showEncounterText(scene: BattleScene, contentKey: string, delay: number | null = null, callbackDelay: number = 0, prompt: boolean = true, promptDelay: number | null = null): Promise<void> { export function showEncounterText(contentKey: string, delay: number | null = null, callbackDelay: number = 0, prompt: boolean = true, promptDelay: number | null = null): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const text: string | null = getEncounterText(scene, contentKey); const text: string | null = getEncounterText(contentKey);
scene.ui.showText(text ?? "", delay, () => resolve(), callbackDelay, prompt, promptDelay); globalScene.ui.showText(text ?? "", delay, () => resolve(), callbackDelay, prompt, promptDelay);
}); });
} }
@ -77,10 +78,10 @@ export function showEncounterText(scene: BattleScene, contentKey: string, delay:
* @param speakerContentKey * @param speakerContentKey
* @param callbackDelay * @param callbackDelay
*/ */
export function showEncounterDialogue(scene: BattleScene, textContentKey: string, speakerContentKey: string, delay: number | null = null, callbackDelay: number = 0): Promise<void> { export function showEncounterDialogue(textContentKey: string, speakerContentKey: string, delay: number | null = null, callbackDelay: number = 0): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const text: string | null = getEncounterText(scene, textContentKey); const text: string | null = getEncounterText(textContentKey);
const speaker: string | null = getEncounterText(scene, speakerContentKey); const speaker: string | null = getEncounterText(speakerContentKey);
scene.ui.showDialogue(text ?? "", speaker ?? "", delay, () => resolve(), callbackDelay); globalScene.ui.showDialogue(text ?? "", speaker ?? "", delay, () => resolve(), callbackDelay);
}); });
} }

View File

@ -1,34 +1,40 @@
import Battle, { BattlerIndex, BattleType } from "#app/battle"; import type Battle from "#app/battle";
import { BattlerIndex, BattleType } from "#app/battle";
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes"; import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import Pokemon, { AiType, FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; import type { AiType, PlayerPokemon } from "#app/field/pokemon";
import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import type Pokemon from "#app/field/pokemon";
import { FieldPosition, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
import { ModifierPoolType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases";
import PokemonData from "#app/system/pokemon-data"; import type PokemonData from "#app/system/pokemon-data";
import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { PartyOption, PartyUiMode, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
import { PartyUiMode } from "#app/ui/party-ui-handler";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type"; import type { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { TrainerType } from "#enums/trainer-type"; import type { TrainerType } from "#enums/trainer-type";
import i18next from "i18next"; import i18next from "i18next";
import BattleScene from "#app/battle-scene";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import { Gender } from "#app/data/gender"; import type { Gender } from "#app/data/gender";
import { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { Status } from "#app/data/status-effect"; import { Status } from "#app/data/status-effect";
import { TrainerConfig, trainerConfigs, TrainerSlot } from "#app/data/trainer-config"; import type { TrainerConfig } from "#app/data/trainer-config";
import PokemonSpecies from "#app/data/pokemon-species"; import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config";
import { Egg, IEggOptions } from "#app/data/egg"; import type PokemonSpecies from "#app/data/pokemon-species";
import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import type { IEggOptions } from "#app/data/egg";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import { Egg } from "#app/data/egg";
import type { CustomPokemonData } from "#app/data/custom-pokemon-data";
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { MovePhase } from "#app/phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
@ -36,34 +42,35 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { GameOverPhase } from "#app/phases/game-over-phase"; import { GameOverPhase } from "#app/phases/game-over-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { PartyExpPhase } from "#app/phases/party-exp-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase";
import { Variant } from "#app/data/variant"; import type { Variant } from "#app/data/variant";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { globalScene } from "#app/global-scene";
/** /**
* Animates exclamation sprite over trainer's head at start of encounter * Animates exclamation sprite over trainer's head at start of encounter
* @param scene * @param scene
*/ */
export function doTrainerExclamation(scene: BattleScene) { export function doTrainerExclamation() {
const exclamationSprite = scene.add.sprite(0, 0, "encounter_exclaim"); const exclamationSprite = globalScene.add.sprite(0, 0, "encounter_exclaim");
exclamationSprite.setName("exclamation"); exclamationSprite.setName("exclamation");
scene.field.add(exclamationSprite); globalScene.field.add(exclamationSprite);
scene.field.moveTo(exclamationSprite, scene.field.getAll().length - 1); globalScene.field.moveTo(exclamationSprite, globalScene.field.getAll().length - 1);
exclamationSprite.setVisible(true); exclamationSprite.setVisible(true);
exclamationSprite.setPosition(110, 68); exclamationSprite.setPosition(110, 68);
scene.tweens.add({ globalScene.tweens.add({
targets: exclamationSprite, targets: exclamationSprite,
y: "-=25", y: "-=25",
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
duration: 300, duration: 300,
yoyo: true, yoyo: true,
onComplete: () => { onComplete: () => {
scene.time.delayedCall(800, () => { globalScene.time.delayedCall(800, () => {
scene.field.remove(exclamationSprite, true); globalScene.field.remove(exclamationSprite, true);
}); });
} }
}); });
scene.playSound("battle_anims/GEN8- Exclaim", { volume: 0.7 }); globalScene.playSound("battle_anims/GEN8- Exclaim", { volume: 0.7 });
} }
export interface EnemyPokemonConfig { export interface EnemyPokemonConfig {
@ -114,14 +121,13 @@ export interface EnemyPartyConfig {
* Generates an enemy party for a mystery encounter battle * Generates an enemy party for a mystery encounter battle
* This will override and replace any standard encounter generation logic * This will override and replace any standard encounter generation logic
* Useful for tailoring specific battles to mystery encounters * Useful for tailoring specific battles to mystery encounters
* @param scene Battle Scene
* @param partyConfig Can pass various customizable attributes for the enemy party, see EnemyPartyConfig * @param partyConfig Can pass various customizable attributes for the enemy party, see EnemyPartyConfig
*/ */
export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: EnemyPartyConfig): Promise<void> { export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): Promise<void> {
const loaded: boolean = false; const loaded: boolean = false;
const loadEnemyAssets: Promise<void>[] = []; const loadEnemyAssets: Promise<void>[] = [];
const battle: Battle = scene.currentBattle; const battle: Battle = globalScene.currentBattle;
let doubleBattle: boolean = partyConfig?.doubleBattle ?? false; let doubleBattle: boolean = partyConfig?.doubleBattle ?? false;
@ -130,10 +136,10 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
const partyTrainerConfig = partyConfig?.trainerConfig; const partyTrainerConfig = partyConfig?.trainerConfig;
let trainerConfig: TrainerConfig; let trainerConfig: TrainerConfig;
if (!isNullOrUndefined(trainerType) || partyTrainerConfig) { if (!isNullOrUndefined(trainerType) || partyTrainerConfig) {
scene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.TRAINER_BATTLE; globalScene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.TRAINER_BATTLE;
if (scene.currentBattle.trainer) { if (globalScene.currentBattle.trainer) {
scene.currentBattle.trainer.setVisible(false); globalScene.currentBattle.trainer.setVisible(false);
scene.currentBattle.trainer.destroy(); globalScene.currentBattle.trainer.destroy();
} }
trainerConfig = partyTrainerConfig ? partyTrainerConfig : trainerConfigs[trainerType!]; trainerConfig = partyTrainerConfig ? partyTrainerConfig : trainerConfigs[trainerType!];
@ -141,23 +147,23 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle); const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle);
doubleBattle = doubleTrainer; doubleBattle = doubleTrainer;
const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!(Utils.randSeedInt(2)) : partyConfig.female; const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!(Utils.randSeedInt(2)) : partyConfig.female;
const newTrainer = new Trainer(scene, trainerConfig.trainerType, doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, undefined, undefined, undefined, trainerConfig); const newTrainer = new Trainer(trainerConfig.trainerType, doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, undefined, undefined, undefined, trainerConfig);
newTrainer.x += 300; newTrainer.x += 300;
newTrainer.setVisible(false); newTrainer.setVisible(false);
scene.field.add(newTrainer); globalScene.field.add(newTrainer);
scene.currentBattle.trainer = newTrainer; globalScene.currentBattle.trainer = newTrainer;
loadEnemyAssets.push(newTrainer.loadAssets().then(() => newTrainer.initSprite())); loadEnemyAssets.push(newTrainer.loadAssets().then(() => newTrainer.initSprite()));
battle.enemyLevels = scene.currentBattle.trainer.getPartyLevels(scene.currentBattle.waveIndex); battle.enemyLevels = globalScene.currentBattle.trainer.getPartyLevels(globalScene.currentBattle.waveIndex);
} else { } else {
// Wild // Wild
scene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.WILD_BATTLE; globalScene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.WILD_BATTLE;
const numEnemies = partyConfig?.pokemonConfigs && partyConfig.pokemonConfigs.length > 0 ? partyConfig?.pokemonConfigs?.length : doubleBattle ? 2 : 1; const numEnemies = partyConfig?.pokemonConfigs && partyConfig.pokemonConfigs.length > 0 ? partyConfig?.pokemonConfigs?.length : doubleBattle ? 2 : 1;
battle.enemyLevels = new Array(numEnemies).fill(null).map(() => scene.currentBattle.getLevelForWave()); battle.enemyLevels = new Array(numEnemies).fill(null).map(() => globalScene.currentBattle.getLevelForWave());
} }
scene.getEnemyParty().forEach(enemyPokemon => { globalScene.getEnemyParty().forEach(enemyPokemon => {
scene.field.remove(enemyPokemon, true); globalScene.field.remove(enemyPokemon, true);
}); });
battle.enemyParty = []; battle.enemyParty = [];
battle.double = doubleBattle; battle.double = doubleBattle;
@ -168,7 +174,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc. // levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
// Leaving null/undefined will disable level scaling // Leaving null/undefined will disable level scaling
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0; const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0;
const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0); const additive = Math.max(Math.round((globalScene.currentBattle.waveIndex / 10) * mult), 0);
battle.enemyLevels = battle.enemyLevels.map(level => level + additive); battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
battle.enemyLevels.forEach((level, e) => { battle.enemyLevels.forEach((level, e) => {
@ -184,7 +190,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
dataSource = config.dataSource; dataSource = config.dataSource;
enemySpecies = config.species; enemySpecies = config.species;
isBoss = config.isBoss; isBoss = config.isBoss;
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.TRAINER, isBoss, false, dataSource); battle.enemyParty[e] = globalScene.addEnemyPokemon(enemySpecies, level, TrainerSlot.TRAINER, isBoss, false, dataSource);
} else { } else {
battle.enemyParty[e] = battle.trainer.genPartyMember(e); battle.enemyParty[e] = battle.trainer.genPartyMember(e);
} }
@ -196,17 +202,17 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
enemySpecies = config.species; enemySpecies = config.species;
isBoss = config.isBoss; isBoss = config.isBoss;
if (isBoss) { if (isBoss) {
scene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.BOSS_BATTLE; globalScene.currentBattle.mysteryEncounter!.encounterMode = MysteryEncounterMode.BOSS_BATTLE;
} }
} else { } else {
enemySpecies = scene.randomSpecies(battle.waveIndex, level, true); enemySpecies = globalScene.randomSpecies(battle.waveIndex, level, true);
} }
battle.enemyParty[e] = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, isBoss, false, dataSource); battle.enemyParty[e] = globalScene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, isBoss, false, dataSource);
} }
} }
const enemyPokemon = scene.getEnemyParty()[e]; const enemyPokemon = globalScene.getEnemyParty()[e];
// Make sure basic data is clean // Make sure basic data is clean
enemyPokemon.hp = enemyPokemon.getMaxHp(); enemyPokemon.hp = enemyPokemon.getMaxHp();
@ -219,7 +225,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
} }
if (!loaded && isNullOrUndefined(partyConfig.countAsSeen) || partyConfig.countAsSeen) { if (!loaded && isNullOrUndefined(partyConfig.countAsSeen) || partyConfig.countAsSeen) {
scene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig)); globalScene.gameData.setPokemonSeen(enemyPokemon, true, !!(trainerType || trainerConfig));
} }
if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) { if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) {
@ -257,7 +263,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// Set Boss // Set Boss
if (config.isBoss) { if (config.isBoss) {
let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments! : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments! : globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, level, enemySpecies, true);
if (!isNullOrUndefined(config.bossSegmentModifier)) { if (!isNullOrUndefined(config.bossSegmentModifier)) {
segments += config.bossSegmentModifier; segments += config.bossSegmentModifier;
} }
@ -343,7 +349,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
console.log(`Pokemon: ${enemyPokemon.name}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`); console.log(`Pokemon: ${enemyPokemon.name}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`);
}); });
scene.pushPhase(new MysteryEncounterBattlePhase(scene, partyConfig.disableSwitch)); globalScene.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch));
await Promise.all(loadEnemyAssets); await Promise.all(loadEnemyAssets);
battle.enemyParty.forEach((enemyPokemon_2, e_1) => { battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
@ -357,11 +363,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
} }
}); });
if (!loaded) { if (!loaded) {
regenerateModifierPoolThresholds(scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD); regenerateModifierPoolThresholds(globalScene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD);
const customModifierTypes = partyConfig?.pokemonConfigs const customModifierTypes = partyConfig?.pokemonConfigs
?.filter(config => config?.modifierConfigs) ?.filter(config => config?.modifierConfigs)
.map(config => config.modifierConfigs!); .map(config => config.modifierConfigs!);
scene.generateEnemyModifiers(customModifierTypes); globalScene.generateEnemyModifiers(customModifierTypes);
} }
} }
@ -370,45 +376,42 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
* See: [startOfBattleEffects](IMysteryEncounter.startOfBattleEffects) for more details * See: [startOfBattleEffects](IMysteryEncounter.startOfBattleEffects) for more details
* *
* This promise does not need to be awaited on if called in an encounter onInit (will just load lazily) * This promise does not need to be awaited on if called in an encounter onInit (will just load lazily)
* @param scene
* @param moves * @param moves
*/ */
export function loadCustomMovesForEncounter(scene: BattleScene, moves: Moves | Moves[]) { export function loadCustomMovesForEncounter(moves: Moves | Moves[]) {
moves = Array.isArray(moves) ? moves : [ moves ]; moves = Array.isArray(moves) ? moves : [ moves ];
return Promise.all(moves.map(move => initMoveAnim(scene, move))) return Promise.all(moves.map(move => initMoveAnim(move)))
.then(() => loadMoveAnimAssets(scene, moves)); .then(() => loadMoveAnimAssets(moves));
} }
/** /**
* Will update player money, and animate change (sound optional) * Will update player money, and animate change (sound optional)
* @param scene
* @param changeValue * @param changeValue
* @param playSound * @param playSound
* @param showMessage * @param showMessage
*/ */
export function updatePlayerMoney(scene: BattleScene, changeValue: number, playSound: boolean = true, showMessage: boolean = true) { export function updatePlayerMoney(changeValue: number, playSound: boolean = true, showMessage: boolean = true) {
scene.money = Math.min(Math.max(scene.money + changeValue, 0), Number.MAX_SAFE_INTEGER); globalScene.money = Math.min(Math.max(globalScene.money + changeValue, 0), Number.MAX_SAFE_INTEGER);
scene.updateMoneyText(); globalScene.updateMoneyText();
scene.animateMoneyChanged(false); globalScene.animateMoneyChanged(false);
if (playSound) { if (playSound) {
scene.playSound("se/buy"); globalScene.playSound("se/buy");
} }
if (showMessage) { if (showMessage) {
if (changeValue < 0) { if (changeValue < 0) {
scene.queueMessage(i18next.t("mysteryEncounterMessages:paid_money", { amount: -changeValue }), null, true); globalScene.queueMessage(i18next.t("mysteryEncounterMessages:paid_money", { amount: -changeValue }), null, true);
} else { } else {
scene.queueMessage(i18next.t("mysteryEncounterMessages:receive_money", { amount: changeValue }), null, true); globalScene.queueMessage(i18next.t("mysteryEncounterMessages:receive_money", { amount: changeValue }), null, true);
} }
} }
} }
/** /**
* Converts modifier bullshit to an actual item * Converts modifier bullshit to an actual item
* @param scene Battle Scene
* @param modifier * @param modifier
* @param pregenArgs Can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. * @param pregenArgs Can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc.
*/ */
export function generateModifierType(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierType | null { export function generateModifierType(modifier: () => ModifierType, pregenArgs?: any[]): ModifierType | null {
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier); const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier);
if (!modifierId) { if (!modifierId) {
return null; return null;
@ -419,19 +422,18 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie
// Populates item id and tier (order matters) // Populates item id and tier (order matters)
result = result result = result
.withIdFromFunc(modifierTypes[modifierId]) .withIdFromFunc(modifierTypes[modifierId])
.withTierFromPool(ModifierPoolType.PLAYER, scene.getPlayerParty()); .withTierFromPool(ModifierPoolType.PLAYER, globalScene.getPlayerParty());
return result instanceof ModifierTypeGenerator ? result.generateType(scene.getPlayerParty(), pregenArgs) : result; return result instanceof ModifierTypeGenerator ? result.generateType(globalScene.getPlayerParty(), pregenArgs) : result;
} }
/** /**
* Converts modifier bullshit to an actual item * Converts modifier bullshit to an actual item
* @param scene - Battle Scene
* @param modifier * @param modifier
* @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. * @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc.
*/ */
export function generateModifierTypeOption(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierTypeOption | null { export function generateModifierTypeOption(modifier: () => ModifierType, pregenArgs?: any[]): ModifierTypeOption | null {
const result = generateModifierType(scene, modifier, pregenArgs); const result = generateModifierType(modifier, pregenArgs);
if (result) { if (result) {
return new ModifierTypeOption(result, 0); return new ModifierTypeOption(result, 0);
} }
@ -440,30 +442,29 @@ export function generateModifierTypeOption(scene: BattleScene, modifier: () => M
/** /**
* This function is intended for use inside onPreOptionPhase() of an encounter option * This function is intended for use inside onPreOptionPhase() of an encounter option
* @param scene
* @param onPokemonSelected - Any logic that needs to be performed when Pokemon is chosen * @param onPokemonSelected - Any logic that needs to be performed when Pokemon is chosen
* If a second option needs to be selected, onPokemonSelected should return a OptionSelectItem[] object * If a second option needs to be selected, onPokemonSelected should return a OptionSelectItem[] object
* @param onPokemonNotSelected - Any logic that needs to be performed if no Pokemon is chosen * @param onPokemonNotSelected - Any logic that needs to be performed if no Pokemon is chosen
* @param selectablePokemonFilter * @param selectablePokemonFilter
*/ */
export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (pokemon: PlayerPokemon) => void | OptionSelectItem[], onPokemonNotSelected?: () => void, selectablePokemonFilter?: PokemonSelectFilter): Promise<boolean> { export function selectPokemonForOption(onPokemonSelected: (pokemon: PlayerPokemon) => void | OptionSelectItem[], onPokemonNotSelected?: () => void, selectablePokemonFilter?: PokemonSelectFilter): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const modeToSetOnExit = scene.ui.getMode(); const modeToSetOnExit = globalScene.ui.getMode();
// Open party screen to choose pokemon // Open party screen to choose pokemon
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => { globalScene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
if (slotIndex < scene.getPlayerParty().length) { if (slotIndex < globalScene.getPlayerParty().length) {
scene.ui.setMode(modeToSetOnExit).then(() => { globalScene.ui.setMode(modeToSetOnExit).then(() => {
const pokemon = scene.getPlayerParty()[slotIndex]; const pokemon = globalScene.getPlayerParty()[slotIndex];
const secondaryOptions = onPokemonSelected(pokemon); const secondaryOptions = onPokemonSelected(pokemon);
if (!secondaryOptions) { if (!secondaryOptions) {
scene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); globalScene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
resolve(true); resolve(true);
return; return;
} }
// There is a second option to choose after selecting the Pokemon // There is a second option to choose after selecting the Pokemon
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
const displayOptions = () => { const displayOptions = () => {
// Always appends a cancel option to bottom of options // Always appends a cancel option to bottom of options
const fullOptions = secondaryOptions.map(option => { const fullOptions = secondaryOptions.map(option => {
@ -471,7 +472,7 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
const onSelect = option.handler; const onSelect = option.handler;
option.handler = () => { option.handler = () => {
onSelect(); onSelect();
scene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); globalScene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
resolve(true); resolve(true);
return true; return true;
}; };
@ -479,13 +480,13 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
}).concat({ }).concat({
label: i18next.t("menu:cancel"), label: i18next.t("menu:cancel"),
handler: () => { handler: () => {
scene.ui.clearText(); globalScene.ui.clearText();
scene.ui.setMode(modeToSetOnExit); globalScene.ui.setMode(modeToSetOnExit);
resolve(false); resolve(false);
return true; return true;
}, },
onHover: () => { onHover: () => {
showEncounterText(scene, i18next.t("mysteryEncounterMessages:cancel_option"), 0, 0, false); showEncounterText(i18next.t("mysteryEncounterMessages:cancel_option"), 0, 0, false);
} }
}); });
@ -500,19 +501,19 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
if (fullOptions[0].onHover) { if (fullOptions[0].onHover) {
fullOptions[0].onHover(); fullOptions[0].onHover();
} }
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); globalScene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
}; };
const textPromptKey = scene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt; const textPromptKey = globalScene.currentBattle.mysteryEncounter?.selectedOption?.dialogue?.secondOptionPrompt;
if (!textPromptKey) { if (!textPromptKey) {
displayOptions(); displayOptions();
} else { } else {
showEncounterText(scene, textPromptKey).then(() => displayOptions()); showEncounterText(textPromptKey).then(() => displayOptions());
} }
}); });
}); });
} else { } else {
scene.ui.setMode(modeToSetOnExit).then(() => { globalScene.ui.setMode(modeToSetOnExit).then(() => {
if (onPokemonNotSelected) { if (onPokemonNotSelected) {
onPokemonNotSelected(); onPokemonNotSelected();
} }
@ -529,33 +530,33 @@ interface PokemonAndOptionSelected {
} }
/** /**
* This function is intended for use inside onPreOptionPhase() of an encounter option * This function is intended for use inside `onPreOptionPhase()` of an encounter option
* @param scene *
* If a second option needs to be selected, onPokemonSelected should return a OptionSelectItem[] object * If a second option needs to be selected, `onPokemonSelected` should return a {@linkcode OptionSelectItem}`[]` object
* @param options * @param options
* @param optionSelectPromptKey * @param optionSelectPromptKey
* @param selectablePokemonFilter * @param selectablePokemonFilter
* @param onHoverOverCancelOption * @param onHoverOverCancelOption
*/ */
export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelectItem[], optionSelectPromptKey: string, selectablePokemonFilter?: PokemonSelectFilter, onHoverOverCancelOption?: () => void): Promise<PokemonAndOptionSelected | null> { export function selectOptionThenPokemon(options: OptionSelectItem[], optionSelectPromptKey: string, selectablePokemonFilter?: PokemonSelectFilter, onHoverOverCancelOption?: () => void): Promise<PokemonAndOptionSelected | null> {
return new Promise<PokemonAndOptionSelected | null>(resolve => { return new Promise<PokemonAndOptionSelected | null>(resolve => {
const modeToSetOnExit = scene.ui.getMode(); const modeToSetOnExit = globalScene.ui.getMode();
const displayOptions = (config: OptionSelectConfig) => { const displayOptions = (config: OptionSelectConfig) => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
if (!optionSelectPromptKey) { if (!optionSelectPromptKey) {
// Do hover over the starting selection option // Do hover over the starting selection option
if (fullOptions[0].onHover) { if (fullOptions[0].onHover) {
fullOptions[0].onHover(); fullOptions[0].onHover();
} }
scene.ui.setMode(Mode.OPTION_SELECT, config); globalScene.ui.setMode(Mode.OPTION_SELECT, config);
} else { } else {
showEncounterText(scene, optionSelectPromptKey).then(() => { showEncounterText(optionSelectPromptKey).then(() => {
// Do hover over the starting selection option // Do hover over the starting selection option
if (fullOptions[0].onHover) { if (fullOptions[0].onHover) {
fullOptions[0].onHover(); fullOptions[0].onHover();
} }
scene.ui.setMode(Mode.OPTION_SELECT, config); globalScene.ui.setMode(Mode.OPTION_SELECT, config);
}); });
} }
}); });
@ -563,10 +564,10 @@ export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelec
const selectPokemonAfterOption = (selectedOptionIndex: number) => { const selectPokemonAfterOption = (selectedOptionIndex: number) => {
// Open party screen to choose a Pokemon // Open party screen to choose a Pokemon
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => { globalScene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
if (slotIndex < scene.getPlayerParty().length) { if (slotIndex < globalScene.getPlayerParty().length) {
// Pokemon and option selected // Pokemon and option selected
scene.ui.setMode(modeToSetOnExit).then(() => { globalScene.ui.setMode(modeToSetOnExit).then(() => {
const result: PokemonAndOptionSelected = { selectedPokemonIndex: slotIndex, selectedOptionIndex: selectedOptionIndex }; const result: PokemonAndOptionSelected = { selectedPokemonIndex: slotIndex, selectedOptionIndex: selectedOptionIndex };
resolve(result); resolve(result);
}); });
@ -590,8 +591,8 @@ export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelec
}).concat({ }).concat({
label: i18next.t("menu:cancel"), label: i18next.t("menu:cancel"),
handler: () => { handler: () => {
scene.ui.clearText(); globalScene.ui.clearText();
scene.ui.setMode(modeToSetOnExit); globalScene.ui.setMode(modeToSetOnExit);
resolve(null); resolve(null);
return true; return true;
}, },
@ -599,7 +600,7 @@ export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelec
if (onHoverOverCancelOption) { if (onHoverOverCancelOption) {
onHoverOverCancelOption(); onHoverOverCancelOption();
} }
showEncounterText(scene, i18next.t("mysteryEncounterMessages:cancel_option"), 0, 0, false); showEncounterText(i18next.t("mysteryEncounterMessages:cancel_option"), 0, 0, false);
} }
}); });
@ -617,27 +618,26 @@ export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelec
/** /**
* Will initialize reward phases to follow the mystery encounter * Will initialize reward phases to follow the mystery encounter
* Can have shop displayed or skipped * Can have shop displayed or skipped
* @param scene - Battle Scene
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers * @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
* @param eggRewards * @param eggRewards
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before {@linkcode MysteryEncounterRewardsPhase}) * @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before {@linkcode MysteryEncounterRewardsPhase})
*/ */
export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, eggRewards?: IEggOptions[], preRewardsCallback?: Function) { export function setEncounterRewards(customShopRewards?: CustomModifierSettings, eggRewards?: IEggOptions[], preRewardsCallback?: Function) {
scene.currentBattle.mysteryEncounter!.doEncounterRewards = (scene: BattleScene) => { globalScene.currentBattle.mysteryEncounter!.doEncounterRewards = () => {
if (preRewardsCallback) { if (preRewardsCallback) {
preRewardsCallback(); preRewardsCallback();
} }
if (customShopRewards) { if (customShopRewards) {
scene.unshiftPhase(new SelectModifierPhase(scene, 0, undefined, customShopRewards)); globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards));
} else { } else {
scene.tryRemovePhase(p => p instanceof SelectModifierPhase); globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase);
} }
if (eggRewards) { if (eggRewards) {
eggRewards.forEach(eggOptions => { eggRewards.forEach(eggOptions => {
const egg = new Egg(eggOptions); const egg = new Egg(eggOptions);
egg.addEggToGameData(scene); egg.addEggToGameData();
}); });
} }
@ -648,10 +648,11 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
/** /**
* Will initialize exp phases into the phase queue (these are in addition to any combat or other exp earned) * Will initialize exp phases into the phase queue (these are in addition to any combat or other exp earned)
* Exp Share and Exp Balance will still function as normal * Exp Share and Exp Balance will still function as normal
* @param scene - Battle Scene
* @param participantId - id/s of party pokemon that get full exp value. Other party members will receive Exp Share amounts * @param participantId - id/s of party pokemon that get full exp value. Other party members will receive Exp Share amounts
* @param baseExpValue - gives exp equivalent to a pokemon of the wave index's level. * @param baseExpValue - gives exp equivalent to a pokemon of the wave index's level.
*
* Guidelines: * Guidelines:
* ```md
* 36 - Sunkern (lowest in game) * 36 - Sunkern (lowest in game)
* 62-64 - regional starter base evos * 62-64 - regional starter base evos
* 100 - Scyther * 100 - Scyther
@ -660,14 +661,15 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
* 290 - trio legendaries * 290 - trio legendaries
* 340 - box legendaries * 340 - box legendaries
* 608 - Blissey (highest in game) * 608 - Blissey (highest in game)
* ```
* https://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_effort_value_yield_(Generation_IX) * https://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_effort_value_yield_(Generation_IX)
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue * @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
*/ */
export function setEncounterExp(scene: BattleScene, participantId: number | number[], baseExpValue: number, useWaveIndex: boolean = true) { export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex: boolean = true) {
const participantIds = Array.isArray(participantId) ? participantId : [ participantId ]; const participantIds = Array.isArray(participantId) ? participantId : [ participantId ];
scene.currentBattle.mysteryEncounter!.doEncounterExp = (scene: BattleScene) => { globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
scene.unshiftPhase(new PartyExpPhase(scene, baseExpValue, useWaveIndex, new Set(participantIds))); globalScene.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds)));
return true; return true;
}; };
@ -686,60 +688,57 @@ export class OptionSelectSettings {
/** /**
* Can be used to queue a new series of Options to select for an Encounter * Can be used to queue a new series of Options to select for an Encounter
* MUST be used only in onOptionPhase, will not work in onPreOptionPhase or onPostOptionPhase * MUST be used only in onOptionPhase, will not work in onPreOptionPhase or onPostOptionPhase
* @param scene
* @param optionSelectSettings * @param optionSelectSettings
*/ */
export function initSubsequentOptionSelect(scene: BattleScene, optionSelectSettings: OptionSelectSettings) { export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) {
scene.pushPhase(new MysteryEncounterPhase(scene, optionSelectSettings)); globalScene.pushPhase(new MysteryEncounterPhase(optionSelectSettings));
} }
/** /**
* Can be used to exit an encounter without any battles or followup * Can be used to exit an encounter without any battles or followup
* Will skip any shops and rewards, and queue the next encounter phase as normal * Will skip any shops and rewards, and queue the next encounter phase as normal
* @param scene
* @param addHealPhase - when true, will add a shop phase to end of encounter with 0 rewards but healing items are available * @param addHealPhase - when true, will add a shop phase to end of encounter with 0 rewards but healing items are available
* @param encounterMode - Can set custom encounter mode if necessary (may be required for forcing Pokemon to return before next phase) * @param encounterMode - Can set custom encounter mode if necessary (may be required for forcing Pokemon to return before next phase)
*/ */
export function leaveEncounterWithoutBattle(scene: BattleScene, addHealPhase: boolean = false, encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE) { export function leaveEncounterWithoutBattle(addHealPhase: boolean = false, encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE) {
scene.currentBattle.mysteryEncounter!.encounterMode = encounterMode; globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.clearPhaseQueueSplice(); globalScene.clearPhaseQueueSplice();
handleMysteryEncounterVictory(scene, addHealPhase); handleMysteryEncounterVictory(addHealPhase);
} }
/** /**
* *
* @param scene
* @param addHealPhase - Adds an empty shop phase to allow player to purchase healing items * @param addHealPhase - Adds an empty shop phase to allow player to purchase healing items
* @param doNotContinue - default `false`. If set to true, will not end the battle and continue to next wave * @param doNotContinue - default `false`. If set to true, will not end the battle and continue to next wave
*/ */
export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) { export function handleMysteryEncounterVictory(addHealPhase: boolean = false, doNotContinue: boolean = false) {
const allowedPkm = scene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle()); const allowedPkm = globalScene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle());
if (allowedPkm.length === 0) { if (allowedPkm.length === 0) {
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.unshiftPhase(new GameOverPhase(scene)); globalScene.unshiftPhase(new GameOverPhase());
return; return;
} }
// If in repeated encounter variant, do nothing // If in repeated encounter variant, do nothing
// Variant must eventually be swapped in order to handle "true" end of the encounter // Variant must eventually be swapped in order to handle "true" end of the encounter
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.continuousEncounter || doNotContinue) { if (encounter.continuousEncounter || doNotContinue) {
return; return;
} else if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) { } else if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
scene.pushPhase(new EggLapsePhase(scene)); globalScene.pushPhase(new EggLapsePhase());
} else if (!scene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) { } else if (!globalScene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) {
scene.pushPhase(new BattleEndPhase(scene, true)); globalScene.pushPhase(new BattleEndPhase(true));
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
scene.pushPhase(new TrainerVictoryPhase(scene)); globalScene.pushPhase(new TrainerVictoryPhase());
} }
if (scene.gameMode.isEndless || !scene.gameMode.isWaveFinal(scene.currentBattle.waveIndex)) { if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
if (!encounter.doContinueEncounter) { if (!encounter.doContinueEncounter) {
// Only lapse eggs once for multi-battle encounters // Only lapse eggs once for multi-battle encounters
scene.pushPhase(new EggLapsePhase(scene)); globalScene.pushPhase(new EggLapsePhase());
} }
} }
} }
@ -747,48 +746,46 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
/** /**
* Similar to {@linkcode handleMysteryEncounterVictory}, but for cases where the player lost a battle or failed a challenge * Similar to {@linkcode handleMysteryEncounterVictory}, but for cases where the player lost a battle or failed a challenge
* @param scene
* @param addHealPhase * @param addHealPhase
*/ */
export function handleMysteryEncounterBattleFailed(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) { export function handleMysteryEncounterBattleFailed(addHealPhase: boolean = false, doNotContinue: boolean = false) {
const allowedPkm = scene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle()); const allowedPkm = globalScene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle());
if (allowedPkm.length === 0) { if (allowedPkm.length === 0) {
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.unshiftPhase(new GameOverPhase(scene)); globalScene.unshiftPhase(new GameOverPhase());
return; return;
} }
// If in repeated encounter variant, do nothing // If in repeated encounter variant, do nothing
// Variant must eventually be swapped in order to handle "true" end of the encounter // Variant must eventually be swapped in order to handle "true" end of the encounter
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.continuousEncounter || doNotContinue) { if (encounter.continuousEncounter || doNotContinue) {
return; return;
} else if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) { } else if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
scene.pushPhase(new BattleEndPhase(scene, false)); globalScene.pushPhase(new BattleEndPhase(false));
} }
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
if (!encounter.doContinueEncounter) { if (!encounter.doContinueEncounter) {
// Only lapse eggs once for multi-battle encounters // Only lapse eggs once for multi-battle encounters
scene.pushPhase(new EggLapsePhase(scene)); globalScene.pushPhase(new EggLapsePhase());
} }
} }
/** /**
* *
* @param scene
* @param hide - If true, performs ease out and hide visuals. If false, eases in visuals. Defaults to true * @param hide - If true, performs ease out and hide visuals. If false, eases in visuals. Defaults to true
* @param destroy - If true, will destroy visuals ONLY ON HIDE TRANSITION. Does nothing on show. Defaults to true * @param destroy - If true, will destroy visuals ONLY ON HIDE TRANSITION. Does nothing on show. Defaults to true
* @param duration * @param duration
*/ */
export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: boolean = true, destroy: boolean = true, duration: number = 750): Promise<boolean> { export function transitionMysteryEncounterIntroVisuals(hide: boolean = true, destroy: boolean = true, duration: number = 750): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const introVisuals = scene.currentBattle.mysteryEncounter!.introVisuals; const introVisuals = globalScene.currentBattle.mysteryEncounter!.introVisuals;
const enemyPokemon = scene.getEnemyField(); const enemyPokemon = globalScene.getEnemyField();
if (enemyPokemon) { if (enemyPokemon) {
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
} }
if (introVisuals) { if (introVisuals) {
if (!hide) { if (!hide) {
@ -800,7 +797,7 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide:
} }
// Transition // Transition
scene.tweens.add({ globalScene.tweens.add({
targets: [ introVisuals, enemyPokemon ], targets: [ introVisuals, enemyPokemon ],
x: `${hide ? "+" : "-"}=16`, x: `${hide ? "+" : "-"}=16`,
y: `${hide ? "-" : "+"}=16`, y: `${hide ? "-" : "+"}=16`,
@ -809,13 +806,13 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide:
duration, duration,
onComplete: () => { onComplete: () => {
if (hide && destroy) { if (hide && destroy) {
scene.field.remove(introVisuals, true); globalScene.field.remove(introVisuals, true);
enemyPokemon.forEach(pokemon => { enemyPokemon.forEach(pokemon => {
scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
}); });
scene.currentBattle.mysteryEncounter!.introVisuals = undefined; globalScene.currentBattle.mysteryEncounter!.introVisuals = undefined;
} }
resolve(true); resolve(true);
} }
@ -829,11 +826,10 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide:
/** /**
* Will queue moves for any pokemon to use before the first CommandPhase of a battle * Will queue moves for any pokemon to use before the first CommandPhase of a battle
* Mostly useful for allowing {@linkcode MysteryEncounter} enemies to "cheat" and use moves before the first turn * Mostly useful for allowing {@linkcode MysteryEncounter} enemies to "cheat" and use moves before the first turn
* @param scene
*/ */
export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { export function handleMysteryEncounterBattleStartEffects() {
const encounter = scene.currentBattle.mysteryEncounter; const encounter = globalScene.currentBattle.mysteryEncounter;
if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { if (globalScene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE && !encounter.startOfBattleEffectsComplete) {
const effects = encounter.startOfBattleEffects; const effects = encounter.startOfBattleEffects;
effects.forEach(effect => { effects.forEach(effect => {
let source; let source;
@ -841,24 +837,24 @@ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
source = effect.sourcePokemon; source = effect.sourcePokemon;
} else if (!isNullOrUndefined(effect.sourceBattlerIndex)) { } else if (!isNullOrUndefined(effect.sourceBattlerIndex)) {
if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) { if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) {
source = scene.getEnemyField()[0]; source = globalScene.getEnemyField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) { } else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) {
source = scene.getEnemyField()[0]; source = globalScene.getEnemyField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) { } else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) {
source = scene.getEnemyField()[1]; source = globalScene.getEnemyField()[1];
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) { } else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) {
source = scene.getPlayerField()[0]; source = globalScene.getPlayerField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) { } else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) {
source = scene.getPlayerField()[1]; source = globalScene.getPlayerField()[1];
} }
} else { } else {
source = scene.getEnemyField()[0]; source = globalScene.getEnemyField()[0];
} }
scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.ignorePp)); globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
}); });
// Pseudo turn end phase to reset flinch states, Endure, etc. // Pseudo turn end phase to reset flinch states, Endure, etc.
scene.pushPhase(new MysteryEncounterBattleStartCleanupPhase(scene)); globalScene.pushPhase(new MysteryEncounterBattleStartCleanupPhase());
encounter.startOfBattleEffectsComplete = true; encounter.startOfBattleEffectsComplete = true;
} }
@ -867,13 +863,12 @@ export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
/** /**
* Can queue extra phases or logic during {@linkcode TurnInitPhase} * Can queue extra phases or logic during {@linkcode TurnInitPhase}
* Should mostly just be used for injecting custom phases into the battle system on turn start * Should mostly just be used for injecting custom phases into the battle system on turn start
* @param scene
* @return boolean - if true, will skip the remainder of the {@linkcode TurnInitPhase} * @return boolean - if true, will skip the remainder of the {@linkcode TurnInitPhase}
*/ */
export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): boolean { export function handleMysteryEncounterTurnStartEffects(): boolean {
const encounter = scene.currentBattle.mysteryEncounter; const encounter = globalScene.currentBattle.mysteryEncounter;
if (scene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.onTurnStart) { if (globalScene.currentBattle.isBattleMysteryEncounter() && encounter && encounter.onTurnStart) {
return encounter.onTurnStart(scene); return encounter.onTurnStart();
} }
return false; return false;
@ -882,10 +877,9 @@ export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): bool
/** /**
* TODO: remove once encounter spawn rate is finalized * TODO: remove once encounter spawn rate is finalized
* Just a helper function to calculate aggregate stats for MEs in a Classic run * Just a helper function to calculate aggregate stats for MEs in a Classic run
* @param scene
* @param baseSpawnWeight * @param baseSpawnWeight
*/ */
export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) { export function calculateMEAggregateStats(baseSpawnWeight: number) {
const numRuns = 1000; const numRuns = 1000;
let run = 0; let run = 0;
const biomes = Object.keys(Biome).filter(key => isNaN(Number(key))); const biomes = Object.keys(Biome).filter(key => isNaN(Number(key)));
@ -898,9 +892,9 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
const encountersByBiome = new Map<string, number>(biomes.map(b => [ b, 0 ])); const encountersByBiome = new Map<string, number>(biomes.map(b => [ b, 0 ]));
const validMEfloorsByBiome = new Map<string, number>(biomes.map(b => [ b, 0 ])); const validMEfloorsByBiome = new Map<string, number>(biomes.map(b => [ b, 0 ]));
let currentBiome = Biome.TOWN; let currentBiome = Biome.TOWN;
let currentArena = scene.newArena(currentBiome); let currentArena = globalScene.newArena(currentBiome);
scene.setSeed(Utils.randomString(24)); globalScene.setSeed(Utils.randomString(24));
scene.resetSeed(); globalScene.resetSeed();
for (let i = 10; i < 180; i++) { for (let i = 10; i < 180; i++) {
// Boss // Boss
if (i % 10 === 0) { if (i % 10 === 0) {
@ -911,7 +905,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
if (i % 10 === 1) { if (i % 10 === 1) {
if (Array.isArray(biomeLinks[currentBiome])) { if (Array.isArray(biomeLinks[currentBiome])) {
let biomes: Biome[]; let biomes: Biome[];
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[])
.filter(b => { .filter(b => {
return !Array.isArray(b) || !Utils.randSeedInt(b[1]); return !Array.isArray(b) || !Utils.randSeedInt(b[1]);
@ -932,20 +926,20 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
if (!(i % 50)) { if (!(i % 50)) {
currentBiome = Biome.END; currentBiome = Biome.END;
} else { } else {
currentBiome = scene.generateRandomBiome(i); currentBiome = globalScene.generateRandomBiome(i);
} }
} }
currentArena = scene.newArena(currentBiome); currentArena = globalScene.newArena(currentBiome);
} }
// Fixed battle // Fixed battle
if (scene.gameMode.isFixedBattle(i)) { if (globalScene.gameMode.isFixedBattle(i)) {
continue; continue;
} }
// Trainer // Trainer
if (scene.gameMode.isWaveTrainer(i, currentArena)) { if (globalScene.gameMode.isWaveTrainer(i, currentArena)) {
continue; continue;
} }
@ -995,7 +989,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
const encountersByBiomeRuns: Map<string, number>[] = []; const encountersByBiomeRuns: Map<string, number>[] = [];
const validFloorsByBiome: Map<string, number>[] = []; const validFloorsByBiome: Map<string, number>[] = [];
while (run < numRuns) { while (run < numRuns) {
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const [ numEncounters, encountersByBiome, validMEfloorsByBiome ] = calculateNumEncounters(); const [ numEncounters, encountersByBiome, validMEfloorsByBiome ] = calculateNumEncounters();
encounterRuns.push(numEncounters); encounterRuns.push(numEncounters);
encountersByBiomeRuns.push(encountersByBiome); encountersByBiomeRuns.push(encountersByBiome);
@ -1047,17 +1041,16 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
/** /**
* TODO: remove once encounter spawn rate is finalized * TODO: remove once encounter spawn rate is finalized
* Just a helper function to calculate aggregate stats for MEs in a Classic run * Just a helper function to calculate aggregate stats for MEs in a Classic run
* @param scene
* @param luckValue - 0 to 14 * @param luckValue - 0 to 14
*/ */
export function calculateRareSpawnAggregateStats(scene: BattleScene, luckValue: number) { export function calculateRareSpawnAggregateStats(luckValue: number) {
const numRuns = 1000; const numRuns = 1000;
let run = 0; let run = 0;
const calculateNumRareEncounters = (): any[] => { const calculateNumRareEncounters = (): any[] => {
const bossEncountersByRarity = [ 0, 0, 0, 0 ]; const bossEncountersByRarity = [ 0, 0, 0, 0 ];
scene.setSeed(Utils.randomString(24)); globalScene.setSeed(Utils.randomString(24));
scene.resetSeed(); globalScene.resetSeed();
// There are 12 wild boss floors // There are 12 wild boss floors
for (let i = 0; i < 12; i++) { for (let i = 0; i < 12; i++) {
// Roll boss tier // Roll boss tier
@ -1091,7 +1084,7 @@ export function calculateRareSpawnAggregateStats(scene: BattleScene, luckValue:
const encounterRuns: number[][] = []; const encounterRuns: number[][] = [];
while (run < numRuns) { while (run < numRuns) {
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const bossEncountersByRarity = calculateNumRareEncounters(); const bossEncountersByRarity = calculateNumRareEncounters();
encounterRuns.push(bossEncountersByRarity); encounterRuns.push(bossEncountersByRarity);
}, 1000 * run); }, 1000 * run);

View File

@ -1,28 +1,32 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import i18next from "i18next"; import i18next from "i18next";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { PokemonHeldItemModifier } from "#app/modifier/modifier";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor } from "#app/data/pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor } from "#app/data/pokeball";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims"; import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; import type { PartyOption } from "#app/ui/party-ui-handler";
import { PartyUiMode } from "#app/ui/party-ui-handler";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { PermanentStat } from "#enums/stat"; import type { PermanentStat } from "#enums/stat";
import { VictoryPhase } from "#app/phases/victory-phase"; import { VictoryPhase } from "#app/phases/victory-phase";
import { SummaryUiMode } from "#app/ui/summary-ui-handler"; import { SummaryUiMode } from "#app/ui/summary-ui-handler";
import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { Abilities } from "#enums/abilities"; import type { Abilities } from "#enums/abilities";
import type { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
@ -45,7 +49,6 @@ export function getSpriteKeysFromSpecies(species: Species, female?: boolean, for
/** /**
* Gets the sprite key and file root for a given Pokemon (accounts for gender, shiny, variants, forms, and experimental) * Gets the sprite key and file root for a given Pokemon (accounts for gender, shiny, variants, forms, and experimental)
* @param pokemon
*/ */
export function getSpriteKeysFromPokemon(pokemon: Pokemon): { spriteKey: string, fileRoot: string } { export function getSpriteKeysFromPokemon(pokemon: Pokemon): { spriteKey: string, fileRoot: string } {
const spriteKey = pokemon.getSpeciesForm().getSpriteKey(pokemon.getGender() === Gender.FEMALE, pokemon.formIndex, pokemon.shiny, pokemon.variant); const spriteKey = pokemon.getSpeciesForm().getSpriteKey(pokemon.getGender() === Gender.FEMALE, pokemon.formIndex, pokemon.shiny, pokemon.variant);
@ -57,14 +60,13 @@ export function getSpriteKeysFromPokemon(pokemon: Pokemon): { spriteKey: string,
/** /**
* Will never remove the player's last non-fainted Pokemon (if they only have 1). * Will never remove the player's last non-fainted Pokemon (if they only have 1).
* Otherwise, picks a Pokemon completely at random and removes from the party * Otherwise, picks a Pokemon completely at random and removes from the party
* @param scene
* @param isAllowed Default `false`. If `true`, only picks from legal mons. If no legal mons are found (or there is 1, with `doNotReturnLastAllowedMon = true`), will return a mon that is not allowed. * @param isAllowed Default `false`. If `true`, only picks from legal mons. If no legal mons are found (or there is 1, with `doNotReturnLastAllowedMon = true`), will return a mon that is not allowed.
* @param isFainted Default `false`. If `true`, includes fainted mons. * @param isFainted Default `false`. If `true`, includes fainted mons.
* @param doNotReturnLastAllowedMon Default `false`. If `true`, will never return the last unfainted pokemon in the party. Useful when this function is being used to determine what Pokemon to remove from the party (Don't want to remove last unfainted) * @param doNotReturnLastAllowedMon Default `false`. If `true`, will never return the last unfainted pokemon in the party. Useful when this function is being used to determine what Pokemon to remove from the party (Don't want to remove last unfainted)
* @returns * @returns
*/ */
export function getRandomPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false, doNotReturnLastAllowedMon: boolean = false): PlayerPokemon { export function getRandomPlayerPokemon(isAllowed: boolean = false, isFainted: boolean = false, doNotReturnLastAllowedMon: boolean = false): PlayerPokemon {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let chosenIndex: number; let chosenIndex: number;
let chosenPokemon: PlayerPokemon | null = null; let chosenPokemon: PlayerPokemon | null = null;
const fullyLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && (isFainted || !p.isFainted())); const fullyLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && (isFainted || !p.isFainted()));
@ -102,8 +104,8 @@ export function getRandomPlayerPokemon(scene: BattleScene, isAllowed: boolean =
* @param isFainted Default false. If true, includes fainted mons. * @param isFainted Default false. If true, includes fainted mons.
* @returns * @returns
*/ */
export function getHighestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon { export function getHighestLevelPlayerPokemon(isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let pokemon: PlayerPokemon | null = null; let pokemon: PlayerPokemon | null = null;
for (const p of party) { for (const p of party) {
@ -128,8 +130,8 @@ export function getHighestLevelPlayerPokemon(scene: BattleScene, isAllowed: bool
* @param isFainted Default false. If true, includes fainted mons. * @param isFainted Default false. If true, includes fainted mons.
* @returns * @returns
*/ */
export function getHighestStatPlayerPokemon(scene: BattleScene, stat: PermanentStat, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon { export function getHighestStatPlayerPokemon(stat: PermanentStat, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let pokemon: PlayerPokemon | null = null; let pokemon: PlayerPokemon | null = null;
for (const p of party) { for (const p of party) {
@ -153,8 +155,8 @@ export function getHighestStatPlayerPokemon(scene: BattleScene, stat: PermanentS
* @param isFainted Default false. If true, includes fainted mons. * @param isFainted Default false. If true, includes fainted mons.
* @returns * @returns
*/ */
export function getLowestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon { export function getLowestLevelPlayerPokemon(isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let pokemon: PlayerPokemon | null = null; let pokemon: PlayerPokemon | null = null;
for (const p of party) { for (const p of party) {
@ -178,8 +180,8 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, isAllowed: boole
* @param isFainted Default false. If true, includes fainted mons. * @param isFainted Default false. If true, includes fainted mons.
* @returns * @returns
*/ */
export function getHighestStatTotalPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon { export function getHighestStatTotalPlayerPokemon(isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let pokemon: PlayerPokemon | null = null; let pokemon: PlayerPokemon | null = null;
for (const p of party) { for (const p of party) {
@ -253,11 +255,11 @@ export function getRandomSpeciesByStarterCost(starterTiers: number | [number, nu
* @param scene the battle scene * @param scene the battle scene
* @param pokemon the player pokemon to KO * @param pokemon the player pokemon to KO
*/ */
export function koPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon) { export function koPlayerPokemon(pokemon: PlayerPokemon) {
pokemon.hp = 0; pokemon.hp = 0;
pokemon.trySetStatus(StatusEffect.FAINT); pokemon.trySetStatus(StatusEffect.FAINT);
pokemon.updateInfo(); pokemon.updateInfo();
queueEncounterMessage(scene, i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); queueEncounterMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} }
/** /**
@ -269,11 +271,11 @@ export function koPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon) {
* @param value the hp change amount. Positive for heal. Negative for damage * @param value the hp change amount. Positive for heal. Negative for damage
* *
*/ */
function applyHpChangeToPokemon(scene: BattleScene, pokemon: PlayerPokemon, value: number) { function applyHpChangeToPokemon(pokemon: PlayerPokemon, value: number) {
const hpChange = Math.round(pokemon.hp + value); const hpChange = Math.round(pokemon.hp + value);
const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0); const nextHp = Math.max(Math.min(hpChange, pokemon.getMaxHp()), 0);
if (nextHp === 0) { if (nextHp === 0) {
koPlayerPokemon(scene, pokemon); koPlayerPokemon(pokemon);
} else { } else {
pokemon.hp = nextHp; pokemon.hp = nextHp;
} }
@ -286,7 +288,7 @@ function applyHpChangeToPokemon(scene: BattleScene, pokemon: PlayerPokemon, valu
* @param damage the amount of damage to apply * @param damage the amount of damage to apply
* @see {@linkcode applyHpChangeToPokemon} * @see {@linkcode applyHpChangeToPokemon}
*/ */
export function applyDamageToPokemon(scene: BattleScene, pokemon: PlayerPokemon, damage: number) { export function applyDamageToPokemon(pokemon: PlayerPokemon, damage: number) {
if (damage <= 0) { if (damage <= 0) {
console.warn("Healing pokemon with `applyDamageToPokemon` is not recommended! Please use `applyHealToPokemon` instead."); console.warn("Healing pokemon with `applyDamageToPokemon` is not recommended! Please use `applyHealToPokemon` instead.");
} }
@ -294,7 +296,7 @@ export function applyDamageToPokemon(scene: BattleScene, pokemon: PlayerPokemon,
if (pokemon.isAllowedInBattle() && pokemon.hp - damage <= 0) { if (pokemon.isAllowedInBattle() && pokemon.hp - damage <= 0) {
damage = pokemon.hp - 1; damage = pokemon.hp - 1;
} }
applyHpChangeToPokemon(scene, pokemon, -damage); applyHpChangeToPokemon(pokemon, -damage);
} }
/** /**
@ -304,12 +306,12 @@ export function applyDamageToPokemon(scene: BattleScene, pokemon: PlayerPokemon,
* @param heal the amount of heal to apply * @param heal the amount of heal to apply
* @see {@linkcode applyHpChangeToPokemon} * @see {@linkcode applyHpChangeToPokemon}
*/ */
export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, heal: number) { export function applyHealToPokemon(pokemon: PlayerPokemon, heal: number) {
if (heal <= 0) { if (heal <= 0) {
console.warn("Damaging pokemon with `applyHealToPokemon` is not recommended! Please use `applyDamageToPokemon` instead."); console.warn("Damaging pokemon with `applyHealToPokemon` is not recommended! Please use `applyDamageToPokemon` instead.");
} }
applyHpChangeToPokemon(scene, pokemon, heal); applyHpChangeToPokemon(pokemon, heal);
} }
/** /**
@ -320,11 +322,11 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
*/ */
export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) { export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE() const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE()
.generateType(pokemon.scene.getPlayerParty(), [ value ]) .generateType(globalScene.getPlayerParty(), [ value ])
?.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 pokemon.scene.addModifier(modifier, false, false, false, true); await globalScene.addModifier(modifier, false, false, false, true);
pokemon.calculateStats(); pokemon.calculateStats();
} }
} }
@ -337,10 +339,10 @@ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: numb
* @param modType * @param modType
* @param fallbackModifierType * @param fallbackModifierType
*/ */
export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) { export async function applyModifierTypeToPlayerPokemon(pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) {
// Check if the Pokemon has max stacks of that item already // Check if the Pokemon has max stacks of that item already
const modifier = modType.newModifier(pokemon); const modifier = modType.newModifier(pokemon);
const existing = scene.findModifier(m => ( const existing = globalScene.findModifier(m => (
m instanceof PokemonHeldItemModifier && m instanceof PokemonHeldItemModifier &&
m.type.id === modType.id && m.type.id === modType.id &&
m.pokemonId === pokemon.id && m.pokemonId === pokemon.id &&
@ -348,16 +350,16 @@ export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokem
)) as PokemonHeldItemModifier; )) as PokemonHeldItemModifier;
// At max stacks // At max stacks
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
if (!fallbackModifierType) { if (!fallbackModifierType) {
return; return;
} }
// Apply fallback // Apply fallback
return applyModifierTypeToPlayerPokemon(scene, pokemon, fallbackModifierType); return applyModifierTypeToPlayerPokemon(pokemon, fallbackModifierType);
} }
await scene.addModifier(modifier, false, false, false, true); await globalScene.addModifier(modifier, false, false, false, true);
} }
/** /**
@ -371,7 +373,7 @@ export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokem
* @param pokeballType * @param pokeballType
* @param ballTwitchRate - can pass custom ball catch rates (for special events, like safari) * @param ballTwitchRate - can pass custom ball catch rates (for special events, like safari)
*/ */
export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon, pokeballType: PokeballType, ballTwitchRate?: number): Promise<boolean> { export function trainerThrowPokeball(pokemon: EnemyPokemon, pokeballType: PokeballType, ballTwitchRate?: number): Promise<boolean> {
const originalY: number = pokemon.y; const originalY: number = pokemon.y;
if (!ballTwitchRate) { if (!ballTwitchRate) {
@ -386,43 +388,43 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
const fpOffset = pokemon.getFieldPositionOffset(); const fpOffset = pokemon.getFieldPositionOffset();
const pokeballAtlasKey = getPokeballAtlasKey(pokeballType); const pokeballAtlasKey = getPokeballAtlasKey(pokeballType);
const pokeball: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 25, "pb", pokeballAtlasKey); const pokeball: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(16 + 75, 80 + 25, "pb", pokeballAtlasKey);
pokeball.setOrigin(0.5, 0.625); pokeball.setOrigin(0.5, 0.625);
scene.field.add(pokeball); globalScene.field.add(pokeball);
scene.time.delayedCall(300, () => { globalScene.time.delayedCall(300, () => {
scene.field.moveBelow(pokeball as Phaser.GameObjects.GameObject, pokemon); globalScene.field.moveBelow(pokeball as Phaser.GameObjects.GameObject, pokemon);
}); });
return new Promise(resolve => { return new Promise(resolve => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
scene.time.delayedCall(512, () => { globalScene.time.delayedCall(512, () => {
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
// Trainer throw frames // Trainer throw frames
scene.trainer.setFrame("2"); globalScene.trainer.setFrame("2");
scene.time.delayedCall(256, () => { globalScene.time.delayedCall(256, () => {
scene.trainer.setFrame("3"); globalScene.trainer.setFrame("3");
scene.time.delayedCall(768, () => { globalScene.time.delayedCall(768, () => {
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
}); });
}); });
// Pokeball move and catch logic // Pokeball move and catch logic
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
x: { value: 236 + fpOffset[0], ease: "Linear" }, x: { value: 236 + fpOffset[0], ease: "Linear" },
y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" }, y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" },
duration: 500, duration: 500,
onComplete: () => { onComplete: () => {
pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
scene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); globalScene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokemon.tint(getPokeballTintColor(pokeballType)); pokemon.tint(getPokeballTintColor(pokeballType));
addPokeballOpenParticles(scene, pokeball.x, pokeball.y, pokeballType); addPokeballOpenParticles(pokeball.x, pokeball.y, pokeballType);
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 500, duration: 500,
ease: "Sine.easeIn", ease: "Sine.easeIn",
@ -431,13 +433,13 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
onComplete: () => { onComplete: () => {
pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
pokemon.setVisible(false); pokemon.setVisible(false);
scene.playSound("se/pb_catch"); globalScene.playSound("se/pb_catch");
scene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}`)); globalScene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}`));
const doShake = () => { const doShake = () => {
let shakeCount = 0; let shakeCount = 0;
const pbX = pokeball.x; const pbX = pokeball.x;
const shakeCounter = scene.tweens.addCounter({ const shakeCounter = globalScene.tweens.addCounter({
from: 0, from: 0,
to: 1, to: 1,
repeat: 4, repeat: 4,
@ -456,30 +458,30 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
onRepeat: () => { onRepeat: () => {
if (!pokemon.species.isObtainable()) { if (!pokemon.species.isObtainable()) {
shakeCounter.stop(); shakeCounter.stop();
failCatch(scene, pokemon, originalY, pokeball, pokeballType).then(() => resolve(false)); failCatch(pokemon, originalY, pokeball, pokeballType).then(() => resolve(false));
} else if (shakeCount++ < 3) { } else if (shakeCount++ < 3) {
if (randSeedInt(65536) < ballTwitchRate) { if (randSeedInt(65536) < ballTwitchRate) {
scene.playSound("se/pb_move"); globalScene.playSound("se/pb_move");
} else { } else {
shakeCounter.stop(); shakeCounter.stop();
failCatch(scene, pokemon, originalY, pokeball, pokeballType).then(() => resolve(false)); failCatch(pokemon, originalY, pokeball, pokeballType).then(() => resolve(false));
} }
} else { } else {
scene.playSound("se/pb_lock"); globalScene.playSound("se/pb_lock");
addPokeballCaptureStars(scene, pokeball); addPokeballCaptureStars(pokeball);
const pbTint = scene.add.sprite(pokeball.x, pokeball.y, "pb", "pb"); const pbTint = globalScene.add.sprite(pokeball.x, pokeball.y, "pb", "pb");
pbTint.setOrigin(pokeball.originX, pokeball.originY); pbTint.setOrigin(pokeball.originX, pokeball.originY);
pbTint.setTintFill(0); pbTint.setTintFill(0);
pbTint.setAlpha(0); pbTint.setAlpha(0);
scene.field.add(pbTint); globalScene.field.add(pbTint);
scene.tweens.add({ globalScene.tweens.add({
targets: pbTint, targets: pbTint,
alpha: 0.375, alpha: 0.375,
duration: 200, duration: 200,
easing: "Sine.easeOut", easing: "Sine.easeOut",
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pbTint, targets: pbTint,
alpha: 0, alpha: 0,
duration: 200, duration: 200,
@ -491,12 +493,12 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
} }
}, },
onComplete: () => { onComplete: () => {
catchPokemon(scene, pokemon, pokeball, pokeballType).then(() => resolve(true)); catchPokemon(pokemon, pokeball, pokeballType).then(() => resolve(true));
} }
}); });
}; };
scene.time.delayedCall(250, () => doPokeballBounceAnim(scene, pokeball, 16, 72, 350, doShake)); globalScene.time.delayedCall(250, () => doPokeballBounceAnim(pokeball, 16, 72, 350, doShake));
} }
}); });
} }
@ -513,9 +515,9 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon,
* @param pokeball * @param pokeball
* @param pokeballType * @param pokeballType
*/ */
function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType) { function failCatch(pokemon: EnemyPokemon, originalY: number, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokemon.setY(originalY); pokemon.setY(originalY);
if (pokemon.status?.effect !== StatusEffect.SLEEP) { if (pokemon.status?.effect !== StatusEffect.SLEEP) {
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
@ -526,19 +528,19 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
const pokeballAtlasKey = getPokeballAtlasKey(pokeballType); const pokeballAtlasKey = getPokeballAtlasKey(pokeballType);
pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
scene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); globalScene.time.delayedCall(17, () => pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
scale: 1 scale: 1
}); });
scene.currentBattle.lastUsedPokeball = pokeballType; globalScene.currentBattle.lastUsedPokeball = pokeballType;
removePb(scene, pokeball); removePb(pokeball);
scene.ui.showText(i18next.t("battle:pokemonBrokeFree", { pokemonName: pokemon.getNameToRender() }), null, () => resolve(), null, true); globalScene.ui.showText(i18next.t("battle:pokemonBrokeFree", { pokemonName: pokemon.getNameToRender() }), null, () => resolve(), null, true);
}); });
} }
@ -551,56 +553,56 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
* @param showCatchObtainMessage * @param showCatchObtainMessage
* @param isObtain * @param isObtain
*/ */
export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise<void> { export async function catchPokemon(pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite | null, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise<void> {
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
scene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
if (pokemon.species.subLegendary) { if (pokemon.species.subLegendary) {
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
} }
if (pokemon.species.legendary) { if (pokemon.species.legendary) {
scene.validateAchv(achvs.CATCH_LEGENDARY); globalScene.validateAchv(achvs.CATCH_LEGENDARY);
} }
if (pokemon.species.mythical) { if (pokemon.species.mythical) {
scene.validateAchv(achvs.CATCH_MYTHICAL); globalScene.validateAchv(achvs.CATCH_MYTHICAL);
} }
scene.pokemonInfoContainer.show(pokemon, true); globalScene.pokemonInfoContainer.show(pokemon, true);
scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
return new Promise(resolve => { return new Promise(resolve => {
const doPokemonCatchMenu = () => { const doPokemonCatchMenu = () => {
const end = () => { const end = () => {
// Ensure the pokemon is in the enemy party in all situations // Ensure the pokemon is in the enemy party in all situations
if (!scene.getEnemyParty().some(p => p.id === pokemon.id)) { if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) {
scene.getEnemyParty().push(pokemon); globalScene.getEnemyParty().push(pokemon);
} }
scene.unshiftPhase(new VictoryPhase(scene, pokemon.id, true)); globalScene.unshiftPhase(new VictoryPhase(pokemon.id, true));
scene.pokemonInfoContainer.hide(); globalScene.pokemonInfoContainer.hide();
if (pokeball) { if (pokeball) {
removePb(scene, pokeball); removePb(pokeball);
} }
resolve(); resolve();
}; };
const removePokemon = () => { const removePokemon = () => {
if (pokemon) { if (pokemon) {
scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
} }
}; };
const addToParty = (slotIndex?: number) => { const addToParty = (slotIndex?: number) => {
const newPokemon = pokemon.addToParty(pokeballType, slotIndex); const newPokemon = pokemon.addToParty(pokeballType, slotIndex);
const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); const modifiers = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
if (scene.getPlayerParty().filter(p => p.isShiny()).length === 6) { if (globalScene.getPlayerParty().filter(p => p.isShiny()).length === 6) {
scene.validateAchv(achvs.SHINY_PARTY); globalScene.validateAchv(achvs.SHINY_PARTY);
} }
Promise.all(modifiers.map(m => scene.addModifier(m, true))).then(() => { Promise.all(modifiers.map(m => globalScene.addModifier(m, true))).then(() => {
scene.updateModifiers(true); globalScene.updateModifiers(true);
removePokemon(); removePokemon();
if (newPokemon) { if (newPokemon) {
newPokemon.loadAssets().then(end); newPokemon.loadAssets().then(end);
@ -609,21 +611,21 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
} }
}); });
}; };
Promise.all([ pokemon.hideInfo(), scene.gameData.setPokemonCaught(pokemon) ]).then(() => { Promise.all([ pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon) ]).then(() => {
if (scene.getPlayerParty().length === 6) { if (globalScene.getPlayerParty().length === 6) {
const promptRelease = () => { const promptRelease = () => {
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { globalScene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); globalScene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
scene.ui.setMode(Mode.CONFIRM, () => { globalScene.ui.setMode(Mode.CONFIRM, () => {
const newPokemon = scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); const newPokemon = globalScene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon);
scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { globalScene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
promptRelease(); promptRelease();
}); });
}, false); }, false);
}, () => { }, () => {
scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: integer, _option: PartyOption) => { globalScene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: integer, _option: PartyOption) => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
if (slotIndex < 6) { if (slotIndex < 6) {
addToParty(slotIndex); addToParty(slotIndex);
} else { } else {
@ -632,7 +634,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
}); });
}); });
}, () => { }, () => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
removePokemon(); removePokemon();
end(); end();
}); });
@ -647,7 +649,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
}; };
if (showCatchObtainMessage) { if (showCatchObtainMessage) {
scene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.getNameToRender() }), null, doPokemonCatchMenu, 0, true); globalScene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.getNameToRender() }), null, doPokemonCatchMenu, 0, true);
} else { } else {
doPokemonCatchMenu(); doPokemonCatchMenu();
} }
@ -659,9 +661,9 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
* @param scene * @param scene
* @param pokeball * @param pokeball
*/ */
function removePb(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite) { function removePb(pokeball: Phaser.GameObjects.Sprite) {
if (pokeball) { if (pokeball) {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 250, duration: 250,
delay: 250, delay: 250,
@ -679,11 +681,11 @@ function removePb(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite) {
* @param scene * @param scene
* @param pokemon * @param pokemon
*/ */
export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise<void> { export async function doPokemonFlee(pokemon: EnemyPokemon): Promise<void> {
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
scene.playSound("se/flee"); globalScene.playSound("se/flee");
// Ease pokemon out // Ease pokemon out
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -693,8 +695,8 @@ export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon):
scale: pokemon.getSpriteScale(), scale: pokemon.getSpriteScale(),
onComplete: () => { onComplete: () => {
pokemon.setVisible(false); pokemon.setVisible(false);
scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
showEncounterText(scene, i18next.t("battle:pokemonFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false) showEncounterText(i18next.t("battle:pokemonFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
.then(() => { .then(() => {
resolve(); resolve();
}); });
@ -708,10 +710,10 @@ export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon):
* @param scene * @param scene
* @param pokemon * @param pokemon
*/ */
export function doPlayerFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise<void> { export function doPlayerFlee(pokemon: EnemyPokemon): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
// Ease pokemon out // Ease pokemon out
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -721,8 +723,8 @@ export function doPlayerFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise
scale: pokemon.getSpriteScale(), scale: pokemon.getSpriteScale(),
onComplete: () => { onComplete: () => {
pokemon.setVisible(false); pokemon.setVisible(false);
scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
showEncounterText(scene, i18next.t("battle:playerFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false) showEncounterText(i18next.t("battle:playerFled", { pokemonName: pokemon.getNameToRender() }), null, 600, false)
.then(() => { .then(() => {
resolve(); resolve();
}); });
@ -790,35 +792,35 @@ export function getGoldenBugNetSpecies(level: number): PokemonSpecies {
* @param scene * @param scene
* @param levelAdditiveModifier Default 0. will add +(1 level / 10 waves * levelAdditiveModifier) to the level calculation * @param levelAdditiveModifier Default 0. will add +(1 level / 10 waves * levelAdditiveModifier) to the level calculation
*/ */
export function getEncounterPokemonLevelForWave(scene: BattleScene, levelAdditiveModifier: number = 0) { export function getEncounterPokemonLevelForWave(levelAdditiveModifier: number = 0) {
const currentBattle = scene.currentBattle; const currentBattle = globalScene.currentBattle;
const baseLevel = currentBattle.getLevelForWave(); const baseLevel = currentBattle.getLevelForWave();
// Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier // Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier
return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0); return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0);
} }
export async function addPokemonDataToDexAndValidateAchievements(scene: BattleScene, pokemon: PlayerPokemon) { export async function addPokemonDataToDexAndValidateAchievements(pokemon: PlayerPokemon) {
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
scene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
if (pokemon.species.subLegendary) { if (pokemon.species.subLegendary) {
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
} }
if (pokemon.species.legendary) { if (pokemon.species.legendary) {
scene.validateAchv(achvs.CATCH_LEGENDARY); globalScene.validateAchv(achvs.CATCH_LEGENDARY);
} }
if (pokemon.species.mythical) { if (pokemon.species.mythical) {
scene.validateAchv(achvs.CATCH_MYTHICAL); globalScene.validateAchv(achvs.CATCH_MYTHICAL);
} }
scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
return scene.gameData.setPokemonCaught(pokemon, true, false, false); return globalScene.gameData.setPokemonCaught(pokemon, true, false, false);
} }
/** /**
@ -830,12 +832,12 @@ export async function addPokemonDataToDexAndValidateAchievements(scene: BattleSc
* @param scene * @param scene
* @param invalidSelectionKey * @param invalidSelectionKey
*/ */
export function isPokemonValidForEncounterOptionSelection(pokemon: Pokemon, scene: BattleScene, invalidSelectionKey: string): string | null { export function isPokemonValidForEncounterOptionSelection(pokemon: Pokemon, invalidSelectionKey: string): string | null {
if (!pokemon.isAllowedInChallenge()) { if (!pokemon.isAllowedInChallenge()) {
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null; return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
} }
if (!pokemon.isAllowedInBattle()) { if (!pokemon.isAllowedInBattle()) {
return getEncounterText(scene, invalidSelectionKey) ?? null; return getEncounterText(invalidSelectionKey) ?? null;
} }
return null; return null;

View File

@ -1,8 +1,8 @@
import BattleScene from "#app/battle-scene"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PlayerPokemon } from "#app/field/pokemon";
import { getFrameMs } from "#app/utils"; import { getFrameMs } from "#app/utils";
import { cos, sin } from "#app/field/anims"; import { cos, sin } from "#app/field/anims";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { globalScene } from "#app/global-scene";
export enum TransformationScreenPosition { export enum TransformationScreenPosition {
CENTER, CENTER,
@ -17,10 +17,10 @@ export enum TransformationScreenPosition {
* @param transformPokemon * @param transformPokemon
* @param screenPosition * @param screenPosition
*/ */
export function doPokemonTransformationSequence(scene: BattleScene, previousPokemon: PlayerPokemon, transformPokemon: PlayerPokemon, screenPosition: TransformationScreenPosition) { export function doPokemonTransformationSequence(previousPokemon: PlayerPokemon, transformPokemon: PlayerPokemon, screenPosition: TransformationScreenPosition) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const transformationContainer = scene.fieldUI.getByName("Dream Background") as Phaser.GameObjects.Container; const transformationContainer = globalScene.fieldUI.getByName("Dream Background") as Phaser.GameObjects.Container;
const transformationBaseBg = scene.add.image(0, 0, "default_bg"); const transformationBaseBg = globalScene.add.image(0, 0, "default_bg");
transformationBaseBg.setOrigin(0, 0); transformationBaseBg.setOrigin(0, 0);
transformationBaseBg.setVisible(false); transformationBaseBg.setVisible(false);
transformationContainer.add(transformationBaseBg); transformationContainer.add(transformationBaseBg);
@ -36,8 +36,8 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
const yOffset = screenPosition !== TransformationScreenPosition.CENTER ? -15 : 0; const yOffset = screenPosition !== TransformationScreenPosition.CENTER ? -15 : 0;
const getPokemonSprite = () => { const getPokemonSprite = () => {
const ret = scene.addPokemonSprite(previousPokemon, transformationBaseBg.displayWidth / 2 + xOffset, transformationBaseBg.displayHeight / 2 + yOffset, "pkmn__sub"); const ret = globalScene.addPokemonSprite(previousPokemon, transformationBaseBg.displayWidth / 2 + xOffset, transformationBaseBg.displayHeight / 2 + yOffset, "pkmn__sub");
ret.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret; return ret;
}; };
@ -61,7 +61,7 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(previousPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(previousPokemon.getTeraType()) });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", previousPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", previousPokemon.getSpriteKey());
sprite.setPipelineData("shiny", previousPokemon.shiny); sprite.setPipelineData("shiny", previousPokemon.shiny);
@ -94,14 +94,14 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
}); });
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonSprite, targets: pokemonSprite,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 2000, duration: 2000,
onComplete: () => { onComplete: () => {
doSpiralUpward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset); doSpiralUpward(transformationBaseBg, transformationContainer, xOffset, yOffset);
scene.tweens.addCounter({ globalScene.tweens.addCounter({
from: 0, from: 0,
to: 1, to: 1,
duration: 1000, duration: 1000,
@ -110,26 +110,26 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
}, },
onComplete: () => { onComplete: () => {
pokemonSprite.setVisible(false); pokemonSprite.setVisible(false);
scene.time.delayedCall(700, () => { globalScene.time.delayedCall(700, () => {
doArcDownward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset); doArcDownward(transformationBaseBg, transformationContainer, xOffset, yOffset);
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
pokemonEvoTintSprite.setScale(0.25); pokemonEvoTintSprite.setScale(0.25);
pokemonEvoTintSprite.setVisible(true); pokemonEvoTintSprite.setVisible(true);
doCycle(scene, 1.5, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => { doCycle(1.5, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => {
pokemonEvoSprite.setVisible(true); pokemonEvoSprite.setVisible(true);
doCircleInward(scene, transformationBaseBg, transformationContainer, xOffset, yOffset); doCircleInward(transformationBaseBg, transformationContainer, xOffset, yOffset);
scene.time.delayedCall(900, () => { globalScene.time.delayedCall(900, () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonEvoTintSprite, targets: pokemonEvoTintSprite,
alpha: 0, alpha: 0,
duration: 1500, duration: 1500,
delay: 150, delay: 150,
easing: "Sine.easeIn", easing: "Sine.easeIn",
onComplete: () => { onComplete: () => {
scene.time.delayedCall(3000, () => { globalScene.time.delayedCall(3000, () => {
resolve(); resolve();
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonEvoSprite, targets: pokemonEvoSprite,
alpha: 0, alpha: 0,
duration: 2000, duration: 2000,
@ -163,17 +163,17 @@ export function doPokemonTransformationSequence(scene: BattleScene, previousPoke
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doSpiralUpward(scene: BattleScene, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doSpiralUpward(transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
let f = 0; let f = 0;
scene.tweens.addCounter({ globalScene.tweens.addCounter({
repeat: 64, repeat: 64,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
if (f < 64) { if (f < 64) {
if (!(f & 7)) { if (!(f & 7)) {
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
doSpiralUpwardParticle(scene, (f & 120) * 2 + i * 64, transformationBaseBg, transformationContainer, xOffset, yOffset); doSpiralUpwardParticle((f & 120) * 2 + i * 64, transformationBaseBg, transformationContainer, xOffset, yOffset);
} }
} }
f++; f++;
@ -190,17 +190,17 @@ function doSpiralUpward(scene: BattleScene, transformationBaseBg: Phaser.GameObj
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doArcDownward(scene: BattleScene, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doArcDownward(transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
let f = 0; let f = 0;
scene.tweens.addCounter({ globalScene.tweens.addCounter({
repeat: 96, repeat: 96,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
if (f < 96) { if (f < 96) {
if (f < 6) { if (f < 6) {
for (let i = 0; i < 9; i++) { for (let i = 0; i < 9; i++) {
doArcDownParticle(scene, i * 16, transformationBaseBg, transformationContainer, xOffset, yOffset); doArcDownParticle(i * 16, transformationBaseBg, transformationContainer, xOffset, yOffset);
} }
} }
f++; f++;
@ -217,17 +217,17 @@ function doArcDownward(scene: BattleScene, transformationBaseBg: Phaser.GameObje
* @param pokemonTintSprite * @param pokemonTintSprite
* @param pokemonEvoTintSprite * @param pokemonEvoTintSprite
*/ */
function doCycle(scene: BattleScene, l: number, lastCycle: number, pokemonTintSprite: Phaser.GameObjects.Sprite, pokemonEvoTintSprite: Phaser.GameObjects.Sprite): Promise<boolean> { function doCycle(l: number, lastCycle: number, pokemonTintSprite: Phaser.GameObjects.Sprite, pokemonEvoTintSprite: Phaser.GameObjects.Sprite): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const isLastCycle = l === lastCycle; const isLastCycle = l === lastCycle;
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonTintSprite, targets: pokemonTintSprite,
scale: 0.25, scale: 0.25,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 500 / l, duration: 500 / l,
yoyo: !isLastCycle yoyo: !isLastCycle
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: pokemonEvoTintSprite, targets: pokemonEvoTintSprite,
scale: 1, scale: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
@ -235,7 +235,7 @@ function doCycle(scene: BattleScene, l: number, lastCycle: number, pokemonTintSp
yoyo: !isLastCycle, yoyo: !isLastCycle,
onComplete: () => { onComplete: () => {
if (l < lastCycle) { if (l < lastCycle) {
doCycle(scene, l + 0.5, lastCycle, pokemonTintSprite, pokemonEvoTintSprite).then(success => resolve(success)); doCycle(l + 0.5, lastCycle, pokemonTintSprite, pokemonEvoTintSprite).then(success => resolve(success));
} else { } else {
pokemonTintSprite.setVisible(false); pokemonTintSprite.setVisible(false);
resolve(true); resolve(true);
@ -253,20 +253,20 @@ function doCycle(scene: BattleScene, l: number, lastCycle: number, pokemonTintSp
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doCircleInward(scene: BattleScene, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doCircleInward(transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
let f = 0; let f = 0;
scene.tweens.addCounter({ globalScene.tweens.addCounter({
repeat: 48, repeat: 48,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
if (!f) { if (!f) {
for (let i = 0; i < 16; i++) { for (let i = 0; i < 16; i++) {
doCircleInwardParticle(scene, i * 16, 4, transformationBaseBg, transformationContainer, xOffset, yOffset); doCircleInwardParticle(i * 16, 4, transformationBaseBg, transformationContainer, xOffset, yOffset);
} }
} else if (f === 32) { } else if (f === 32) {
for (let i = 0; i < 16; i++) { for (let i = 0; i < 16; i++) {
doCircleInwardParticle(scene, i * 16, 8, transformationBaseBg, transformationContainer, xOffset, yOffset); doCircleInwardParticle(i * 16, 8, transformationBaseBg, transformationContainer, xOffset, yOffset);
} }
} }
f++; f++;
@ -283,15 +283,15 @@ function doCircleInward(scene: BattleScene, transformationBaseBg: Phaser.GameObj
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doSpiralUpwardParticle(scene: BattleScene, trigIndex: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doSpiralUpwardParticle(trigIndex: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset; const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const particle = scene.add.image(initialX, 0, "evo_sparkle"); const particle = globalScene.add.image(initialX, 0, "evo_sparkle");
transformationContainer.add(particle); transformationContainer.add(particle);
let f = 0; let f = 0;
let amp = 48; let amp = 48;
const particleTimer = scene.tweens.addCounter({ const particleTimer = globalScene.tweens.addCounter({
repeat: -1, repeat: -1,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
@ -328,16 +328,16 @@ function doSpiralUpwardParticle(scene: BattleScene, trigIndex: number, transform
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doArcDownParticle(scene: BattleScene, trigIndex: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doArcDownParticle(trigIndex: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset; const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const particle = scene.add.image(initialX, 0, "evo_sparkle"); const particle = globalScene.add.image(initialX, 0, "evo_sparkle");
particle.setScale(0.5); particle.setScale(0.5);
transformationContainer.add(particle); transformationContainer.add(particle);
let f = 0; let f = 0;
let amp = 8; let amp = 8;
const particleTimer = scene.tweens.addCounter({ const particleTimer = globalScene.tweens.addCounter({
repeat: -1, repeat: -1,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
@ -371,15 +371,15 @@ function doArcDownParticle(scene: BattleScene, trigIndex: number, transformation
* @param xOffset * @param xOffset
* @param yOffset * @param yOffset
*/ */
function doCircleInwardParticle(scene: BattleScene, trigIndex: number, speed: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) { function doCircleInwardParticle(trigIndex: number, speed: number, transformationBaseBg: Phaser.GameObjects.Image, transformationContainer: Phaser.GameObjects.Container, xOffset: number, yOffset: number) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset; const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const initialY = transformationBaseBg.displayHeight / 2 + yOffset; const initialY = transformationBaseBg.displayHeight / 2 + yOffset;
const particle = scene.add.image(initialX, initialY, "evo_sparkle"); const particle = globalScene.add.image(initialX, initialY, "evo_sparkle");
transformationContainer.add(particle); transformationContainer.add(particle);
let amp = 120; let amp = 120;
const particleTimer = scene.tweens.addCounter({ const particleTimer = globalScene.tweens.addCounter({
repeat: -1, repeat: -1,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {

View File

@ -1,7 +1,7 @@
import { globalScene } from "#app/global-scene";
import { CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier"; import { CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier";
import { NumberHolder } from "#app/utils"; import { NumberHolder } from "#app/utils";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import BattleScene from "../battle-scene";
import i18next from "i18next"; import i18next from "i18next";
export const MAX_PER_TYPE_POKEBALLS: integer = 99; export const MAX_PER_TYPE_POKEBALLS: integer = 99;
@ -85,18 +85,17 @@ export function getPokeballTintColor(type: PokeballType): number {
/** /**
* Gets the critical capture chance based on number of mons registered in Dex and modified {@link https://bulbapedia.bulbagarden.net/wiki/Catch_rate Catch rate} * Gets the critical capture chance based on number of mons registered in Dex and modified {@link https://bulbapedia.bulbagarden.net/wiki/Catch_rate Catch rate}
* Formula from {@link https://www.dragonflycave.com/mechanics/gen-vi-vii-capturing Dragonfly Cave Gen 6 Capture Mechanics page} * Formula from {@link https://www.dragonflycave.com/mechanics/gen-vi-vii-capturing Dragonfly Cave Gen 6 Capture Mechanics page}
* @param scene {@linkcode BattleScene} current BattleScene
* @param modifiedCatchRate the modified catch rate as calculated in {@linkcode AttemptCapturePhase} * @param modifiedCatchRate the modified catch rate as calculated in {@linkcode AttemptCapturePhase}
* @returns the chance of getting a critical capture, out of 256 * @returns the chance of getting a critical capture, out of 256
*/ */
export function getCriticalCaptureChance(scene: BattleScene, modifiedCatchRate: number): number { export function getCriticalCaptureChance(modifiedCatchRate: number): number {
if (scene.gameMode.isFreshStartChallenge()) { if (globalScene.gameMode.isFreshStartChallenge()) {
return 0; return 0;
} }
const dexCount = scene.gameData.getSpeciesCount(d => !!d.caughtAttr); const dexCount = globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr);
const catchingCharmMultiplier = new NumberHolder(1); const catchingCharmMultiplier = new NumberHolder(1);
scene.findModifier(m => m instanceof CriticalCatchChanceBoosterModifier)?.apply(catchingCharmMultiplier); globalScene.findModifier(m => m instanceof CriticalCatchChanceBoosterModifier)?.apply(catchingCharmMultiplier);
const dexMultiplier = scene.gameMode.isDaily || dexCount > 800 ? 2.5 const dexMultiplier = globalScene.gameMode.isDaily || dexCount > 800 ? 2.5
: dexCount > 600 ? 2 : dexCount > 600 ? 2
: dexCount > 400 ? 1.5 : dexCount > 400 ? 1.5
: dexCount > 200 ? 1 : dexCount > 200 ? 1
@ -105,7 +104,7 @@ export function getCriticalCaptureChance(scene: BattleScene, modifiedCatchRate:
return Math.floor(catchingCharmMultiplier.value * dexMultiplier * Math.min(255, modifiedCatchRate) / 6); return Math.floor(catchingCharmMultiplier.value * dexMultiplier * Math.min(255, modifiedCatchRate) / 6);
} }
export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite, y1: number, y2: number, baseBounceDuration: number, callback: Function, isCritical: boolean = false) { export function doPokeballBounceAnim(pokeball: Phaser.GameObjects.Sprite, y1: number, y2: number, baseBounceDuration: number, callback: Function, isCritical: boolean = false) {
let bouncePower = 1; let bouncePower = 1;
let bounceYOffset = y1; let bounceYOffset = y1;
let bounceY = y2; let bounceY = y2;
@ -116,13 +115,13 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
let critShakes = 4; let critShakes = 4;
const doBounce = () => { const doBounce = () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
y: y2, y: y2,
duration: bouncePower * baseBounceDuration, duration: bouncePower * baseBounceDuration,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_bounce_1", { volume: bouncePower }); globalScene.playSound("se/pb_bounce_1", { volume: bouncePower });
bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0; bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0;
@ -130,7 +129,7 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
bounceYOffset = yd * bouncePower; bounceYOffset = yd * bouncePower;
bounceY = y2 - bounceYOffset; bounceY = y2 - bounceYOffset;
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
y: bounceY, y: bounceY,
duration: bouncePower * baseBounceDuration, duration: bouncePower * baseBounceDuration,
@ -145,13 +144,13 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
}; };
const doCritShake = () => { const doCritShake = () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
x: x2, x: x2,
duration: 125, duration: 125,
ease: "Linear", ease: "Linear",
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
x: x1, x: x1,
duration: 125, duration: 125,
@ -161,12 +160,12 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
if (critShakes > 0) { if (critShakes > 0) {
doCritShake(); doCritShake();
} else { } else {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
x: x0, x: x0,
duration: 60, duration: 60,
ease: "Linear", ease: "Linear",
onComplete: () => scene.time.delayedCall(500, doBounce) onComplete: () => globalScene.time.delayedCall(500, doBounce)
}); });
} }
} }
@ -176,7 +175,7 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
}; };
if (isCritical) { if (isCritical) {
scene.time.delayedCall(500, doCritShake); globalScene.time.delayedCall(500, doCritShake);
} else { } else {
doBounce(); doBounce();
} }

View File

@ -1,18 +1,19 @@
import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier"; import { PokemonFormChangeItemModifier, TerastallizeModifier } from "../modifier/modifier";
import Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { MoveCategory, allMoves } from "./move"; import { MoveCategory, allMoves } from "./move";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { Constructor, nil } from "#app/utils"; import type { Constructor, nil } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import type { TimeOfDay } from "#enums/time-of-day";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import i18next from "i18next"; import i18next from "i18next";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { Challenges } from "#app/enums/challenges"; import { Challenges } from "#app/enums/challenges";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { globalScene } from "#app/global-scene";
export enum FormChangeItem { export enum FormChangeItem {
NONE, NONE,
@ -259,7 +260,7 @@ export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
} }
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return !!pokemon.scene.findModifier(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id && m.formChangeItem === this.item && m.active === this.active); return !!globalScene.findModifier(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id && m.formChangeItem === this.item && m.active === this.active);
} }
} }
@ -272,7 +273,7 @@ export class SpeciesFormChangeTimeOfDayTrigger extends SpeciesFormChangeTrigger
} }
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return this.timesOfDay.indexOf(pokemon.scene.arena.getTimeOfDay()) > -1; return this.timesOfDay.indexOf(globalScene.arena.getTimeOfDay()) > -1;
} }
} }
@ -335,7 +336,7 @@ export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrig
export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger { export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
const command = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
return !!command?.move && this.movePredicate(command.move.move) === this.used; return !!command?.move && this.movePredicate(command.move.move) === this.used;
} }
} }
@ -348,7 +349,7 @@ export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigg
export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger { export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger {
override canChange(pokemon: Pokemon): boolean { override canChange(pokemon: Pokemon): boolean {
if (pokemon.scene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) { if (globalScene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
return false; return false;
} else { } else {
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song // Meloetta will not transform if it has the ability Sheer Force when using Relic Song
@ -369,7 +370,7 @@ export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
} }
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return this.formKey === pokemon.species.forms[pokemon.scene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)].formKey; return this.formKey === pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)].formKey;
} }
} }
@ -393,7 +394,7 @@ export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
* @returns `true` if the Pokémon can change forms, `false` otherwise * @returns `true` if the Pokémon can change forms, `false` otherwise
*/ */
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id && m.teraType === this.teraType); return !!globalScene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id && m.teraType === this.teraType);
} }
} }
@ -404,7 +405,7 @@ export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
*/ */
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger { export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return !!pokemon.scene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id); return !!globalScene.findModifier(m => m instanceof TerastallizeModifier && m.pokemonId === pokemon.id);
} }
} }
@ -432,8 +433,8 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
* @returns `true` if the Pokemon can change forms, `false` otherwise * @returns `true` if the Pokemon can change forms, `false` otherwise
*/ */
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
const currentWeather = pokemon.scene.arena.weather?.weatherType ?? WeatherType.NONE; const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
const isWeatherSuppressed = pokemon.scene.arena.weather?.isEffectSuppressed(pokemon.scene); const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed; const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
return !isAbilitySuppressed && !isWeatherSuppressed && (pokemon.hasAbility(this.ability) && this.weathers.includes(currentWeather)); return !isAbilitySuppressed && !isWeatherSuppressed && (pokemon.hasAbility(this.ability) && this.weathers.includes(currentWeather));
@ -466,8 +467,8 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
*/ */
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
if (pokemon.hasAbility(this.ability, false, true)) { if (pokemon.hasAbility(this.ability, false, true)) {
const currentWeather = pokemon.scene.arena.weather?.weatherType ?? WeatherType.NONE; const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
const isWeatherSuppressed = pokemon.scene.arena.weather?.isEffectSuppressed(pokemon.scene); const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed; const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
const summonDataAbility = pokemon.summonData.ability; const summonDataAbility = pokemon.summonData.ability;
const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== Abilities.NONE; const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== Abilities.NONE;
@ -510,7 +511,7 @@ export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: Specie
* @returns A {@linkcode SpeciesFormChangeCondition} checking if that species is registered as caught * @returns A {@linkcode SpeciesFormChangeCondition} checking if that species is registered as caught
*/ */
function getSpeciesDependentFormChangeCondition(species: Species): SpeciesFormChangeCondition { function getSpeciesDependentFormChangeCondition(species: Species): SpeciesFormChangeCondition {
return new SpeciesFormChangeCondition(p => !!p.scene.gameData.dexData[species].caughtAttr); return new SpeciesFormChangeCondition(p => !!globalScene.gameData.dexData[species].caughtAttr);
} }
interface PokemonFormChanges { interface PokemonFormChanges {
@ -770,8 +771,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.KYUREM, "", "white", new SpeciesFormChangeItemTrigger(FormChangeItem.LIGHT_STONE), false, getSpeciesDependentFormChangeCondition(Species.RESHIRAM)) new SpeciesFormChange(Species.KYUREM, "", "white", new SpeciesFormChangeItemTrigger(FormChangeItem.LIGHT_STONE), false, getSpeciesDependentFormChangeCondition(Species.RESHIRAM))
], ],
[Species.KELDEO]: [ [Species.KELDEO]: [
new SpeciesFormChange(Species.KELDEO, "ordinary", "resolute", new SpeciesFormChangeMoveLearnedTrigger(Moves.SECRET_SWORD), false, new SpeciesFormChangeCondition((p) => p.scene.gameMode.isDaily !== true)), new SpeciesFormChange(Species.KELDEO, "ordinary", "resolute", new SpeciesFormChangeMoveLearnedTrigger(Moves.SECRET_SWORD), false, new SpeciesFormChangeCondition(() => globalScene.gameMode.isDaily !== true)),
new SpeciesFormChange(Species.KELDEO, "resolute", "ordinary", new SpeciesFormChangeMoveLearnedTrigger(Moves.SECRET_SWORD, false), false, new SpeciesFormChangeCondition((p) => p.scene.gameMode.isDaily !== true)) new SpeciesFormChange(Species.KELDEO, "resolute", "ordinary", new SpeciesFormChangeMoveLearnedTrigger(Moves.SECRET_SWORD, false), false, new SpeciesFormChangeCondition(() => globalScene.gameMode.isDaily !== true))
], ],
[Species.MELOETTA]: [ [Species.MELOETTA]: [
new SpeciesFormChange(Species.MELOETTA, "aria", "pirouette", new MeloettaFormChangePostMoveTrigger(Moves.RELIC_SONG), true), new SpeciesFormChange(Species.MELOETTA, "aria", "pirouette", new MeloettaFormChangePostMoveTrigger(Moves.RELIC_SONG), true),

View File

@ -1,21 +1,25 @@
import { Localizable } from "#app/interfaces/locales"; import type { Localizable } from "#app/interfaces/locales";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
import i18next from "i18next"; import i18next from "i18next";
import BattleScene, { AnySound } from "#app/battle-scene"; import type { AnySound } from "#app/battle-scene";
import { GameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene";
import { StarterMoveset } from "#app/system/game-data"; import type { GameMode } from "#app/game-mode";
import type { StarterMoveset } from "#app/system/game-data";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { uncatchableSpecies } from "#app/data/balance/biomes"; import { uncatchableSpecies } from "#app/data/balance/biomes";
import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { GrowthRate } from "#app/data/exp"; import { GrowthRate } from "#app/data/exp";
import { EvolutionLevel, SpeciesWildEvolutionDelay, pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import type { EvolutionLevel } from "#app/data/balance/pokemon-evolutions";
import { SpeciesWildEvolutionDelay, pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { LevelMoves, pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
import { Stat } from "#enums/stat"; import { pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
import { Variant, VariantSet, variantData } from "#app/data/variant"; import type { Stat } from "#enums/stat";
import type { Variant, VariantSet } from "#app/data/variant";
import { variantData } from "#app/data/variant";
import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
@ -490,34 +494,34 @@ export abstract class PokemonSpeciesForm {
return true; return true;
} }
loadAssets(scene: BattleScene, female: boolean, formIndex?: number, shiny?: boolean, variant?: Variant, startLoad?: boolean): Promise<void> { loadAssets(female: boolean, formIndex?: number, shiny?: boolean, variant?: Variant, startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant); const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant);
scene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant)); globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant));
scene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`); globalScene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`);
scene.load.once(Phaser.Loader.Events.COMPLETE, () => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => {
const originalWarn = console.warn; const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot // Ignore warnings for missing frames, because there will be a lot
console.warn = () => {}; console.warn = () => {};
const frameNames = scene.anims.generateFrameNames(spriteKey, { zeroPad: 4, suffix: ".png", start: 1, end: 400 }); const frameNames = globalScene.anims.generateFrameNames(spriteKey, { zeroPad: 4, suffix: ".png", start: 1, end: 400 });
console.warn = originalWarn; console.warn = originalWarn;
if (!(scene.anims.exists(spriteKey))) { if (!(globalScene.anims.exists(spriteKey))) {
scene.anims.create({ globalScene.anims.create({
key: this.getSpriteKey(female, formIndex, shiny, variant), key: this.getSpriteKey(female, formIndex, shiny, variant),
frames: frameNames, frames: frameNames,
frameRate: 10, frameRate: 10,
repeat: -1 repeat: -1
}); });
} else { } else {
scene.anims.get(spriteKey).frameRate = 10; globalScene.anims.get(spriteKey).frameRate = 10;
} }
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, ""); const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
scene.loadPokemonVariantAssets(spriteKey, spritePath, variant); globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant);
resolve(); resolve();
}); });
if (startLoad) { if (startLoad) {
if (!scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
scene.load.start(); globalScene.load.start();
} }
} else { } else {
resolve(); resolve();
@ -525,21 +529,21 @@ export abstract class PokemonSpeciesForm {
}); });
} }
cry(scene: BattleScene, soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound { cry(soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound {
const cryKey = this.getCryKey(this.formIndex); const cryKey = this.getCryKey(this.formIndex);
let cry: AnySound | null = scene.sound.get(cryKey) as AnySound; let cry: AnySound | null = globalScene.sound.get(cryKey) as AnySound;
if (cry?.pendingRemove) { if (cry?.pendingRemove) {
cry = null; cry = null;
} }
cry = scene.playSound(cry ?? cryKey, soundConfig); cry = globalScene.playSound(cry ?? cryKey, soundConfig);
if (ignorePlay) { if (ignorePlay) {
cry.stop(); cry.stop();
} }
return cry; return cry;
} }
generateCandyColors(scene: BattleScene): number[][] { generateCandyColors(): number[][] {
const sourceTexture = scene.textures.get(this.getSpriteKey(false)); const sourceTexture = globalScene.textures.get(this.getSpriteKey(false));
const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame]; const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame];
const sourceImage = sourceTexture.getSourceImage() as HTMLImageElement; const sourceImage = sourceTexture.getSourceImage() as HTMLImageElement;
@ -582,7 +586,7 @@ export abstract class PokemonSpeciesForm {
const originalRandom = Math.random; const originalRandom = Math.random;
Math.random = () => Phaser.Math.RND.realInRange(0, 1); Math.random = () => Phaser.Math.RND.realInRange(0, 1);
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
paletteColors = QuantizerCelebi.quantize(pixelColors, 2); paletteColors = QuantizerCelebi.quantize(pixelColors, 2);
}, 0, "This result should not vary"); }, 0, "This result should not vary");
@ -959,14 +963,13 @@ export const noStarterFormKeys: string[] = [
/** /**
* Method to get the daily list of starters with Pokerus. * Method to get the daily list of starters with Pokerus.
* @param scene {@linkcode BattleScene} used as part of RNG
* @returns A list of starters with Pokerus * @returns A list of starters with Pokerus
*/ */
export function getPokerusStarters(scene: BattleScene): PokemonSpecies[] { export function getPokerusStarters(): PokemonSpecies[] {
const pokerusStarters: PokemonSpecies[] = []; const pokerusStarters: PokemonSpecies[] = [];
const date = new Date(); const date = new Date();
date.setUTCHours(0, 0, 0, 0); date.setUTCHours(0, 0, 0, 0);
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
while (pokerusStarters.length < POKERUS_STARTER_COUNT) { while (pokerusStarters.length < POKERUS_STARTER_COUNT) {
const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarterCosts)), 10); const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarterCosts)), 10);
const species = getPokemonSpecies(randomSpeciesId); const species = getPokemonSpecies(randomSpeciesId);

View File

@ -1,6 +1,7 @@
import { randIntRange } from "#app/utils"; import { randIntRange } from "#app/utils";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import i18next, { ParseKeys } from "i18next"; import type { ParseKeys } from "i18next";
import i18next from "i18next";
export class Status { export class Status {
public effect: StatusEffect; public effect: StatusEffect;

View File

@ -1,8 +1,8 @@
import Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import Move from "./move"; import type Move from "./move";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { ProtectAttr } from "./move"; import { ProtectAttr } from "./move";
import { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import i18next from "i18next"; import i18next from "i18next";
export enum TerrainType { export enum TerrainType {

View File

@ -1,14 +1,19 @@
import BattleScene, { startingWave } from "#app/battle-scene"; import { startingWave } from "#app/battle-scene";
import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type"; import { globalScene } from "#app/global-scene";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import type { EnemyPokemon } from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokemonSpecies, { getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species"; import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { tmSpecies } from "#app/data/balance/tms"; import { tmSpecies } from "#app/data/balance/tms";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { doubleBattleDialogue } from "#app/data/dialogue"; import { doubleBattleDialogue } from "#app/data/dialogue";
import { PersistentModifier } from "#app/modifier/modifier"; import type { PersistentModifier } from "#app/modifier/modifier";
import { TrainerVariant } from "#app/field/trainer"; import { TrainerVariant } from "#app/field/trainer";
import { getIsInitialized, initI18n } from "#app/plugins/i18n"; import { getIsInitialized, initI18n } from "#app/plugins/i18n";
import i18next from "i18next"; import i18next from "i18next";
@ -169,8 +174,8 @@ export const trainerPartyTemplates = {
RIVAL_6: new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE, false, true), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER)) RIVAL_6: new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE, false, true), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER))
}; };
type PartyTemplateFunc = (scene: BattleScene) => TrainerPartyTemplate; type PartyTemplateFunc = () => TrainerPartyTemplate;
type PartyMemberFunc = (scene: BattleScene, level: integer, strength: PartyMemberStrength) => EnemyPokemon; type PartyMemberFunc = (level: integer, strength: PartyMemberStrength) => EnemyPokemon;
type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[]; type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
export interface PartyMemberFuncs { export interface PartyMemberFuncs {
@ -842,7 +847,7 @@ export class TrainerConfig {
this.setBattleBgm("battle_unova_gym"); this.setBattleBgm("battle_unova_gym");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => { this.setGenModifiersFunc(party => {
const waveIndex = party[0].scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined); return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined);
}); });
@ -1011,28 +1016,28 @@ export class TrainerConfig {
return ret; return ret;
} }
loadAssets(scene: BattleScene, variant: TrainerVariant): Promise<void> { loadAssets(variant: TrainerVariant): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const isDouble = variant === TrainerVariant.DOUBLE; const isDouble = variant === TrainerVariant.DOUBLE;
const trainerKey = this.getSpriteKey(variant === TrainerVariant.FEMALE, false); const trainerKey = this.getSpriteKey(variant === TrainerVariant.FEMALE, false);
const partnerTrainerKey = this.getSpriteKey(true, true); const partnerTrainerKey = this.getSpriteKey(true, true);
scene.loadAtlas(trainerKey, "trainer"); globalScene.loadAtlas(trainerKey, "trainer");
if (isDouble) { if (isDouble) {
scene.loadAtlas(partnerTrainerKey, "trainer"); globalScene.loadAtlas(partnerTrainerKey, "trainer");
} }
scene.load.once(Phaser.Loader.Events.COMPLETE, () => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => {
const originalWarn = console.warn; const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot // Ignore warnings for missing frames, because there will be a lot
console.warn = () => { console.warn = () => {
}; };
const frameNames = scene.anims.generateFrameNames(trainerKey, { const frameNames = globalScene.anims.generateFrameNames(trainerKey, {
zeroPad: 4, zeroPad: 4,
suffix: ".png", suffix: ".png",
start: 1, start: 1,
end: 128 end: 128
}); });
const partnerFrameNames = isDouble const partnerFrameNames = isDouble
? scene.anims.generateFrameNames(partnerTrainerKey, { ? globalScene.anims.generateFrameNames(partnerTrainerKey, {
zeroPad: 4, zeroPad: 4,
suffix: ".png", suffix: ".png",
start: 1, start: 1,
@ -1040,16 +1045,16 @@ export class TrainerConfig {
}) })
: ""; : "";
console.warn = originalWarn; console.warn = originalWarn;
if (!(scene.anims.exists(trainerKey))) { if (!(globalScene.anims.exists(trainerKey))) {
scene.anims.create({ globalScene.anims.create({
key: trainerKey, key: trainerKey,
frames: frameNames, frames: frameNames,
frameRate: 24, frameRate: 24,
repeat: -1 repeat: -1
}); });
} }
if (isDouble && !(scene.anims.exists(partnerTrainerKey))) { if (isDouble && !(globalScene.anims.exists(partnerTrainerKey))) {
scene.anims.create({ globalScene.anims.create({
key: partnerTrainerKey, key: partnerTrainerKey,
frames: partnerFrameNames, frames: partnerFrameNames,
frameRate: 24, frameRate: 24,
@ -1058,8 +1063,8 @@ export class TrainerConfig {
} }
resolve(); resolve();
}); });
if (!scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
scene.load.start(); globalScene.load.start();
} }
}); });
} }
@ -1133,11 +1138,10 @@ interface TrainerConfigs {
/** /**
* The function to get variable strength grunts * The function to get variable strength grunts
* @param scene the singleton scene being passed in
* @returns the correct TrainerPartyTemplate * @returns the correct TrainerPartyTemplate
*/ */
function getEvilGruntPartyTemplate(scene: BattleScene): TrainerPartyTemplate { function getEvilGruntPartyTemplate(): TrainerPartyTemplate {
const waveIndex = scene.currentBattle?.waveIndex; const waveIndex = globalScene.currentBattle?.waveIndex;
if (waveIndex < 40) { if (waveIndex < 40) {
return trainerPartyTemplates.TWO_AVG; return trainerPartyTemplates.TWO_AVG;
} else if (waveIndex < 63) { } else if (waveIndex < 63) {
@ -1151,32 +1155,32 @@ function getEvilGruntPartyTemplate(scene: BattleScene): TrainerPartyTemplate {
} }
} }
function getWavePartyTemplate(scene: BattleScene, ...templates: TrainerPartyTemplate[]) { function getWavePartyTemplate(...templates: TrainerPartyTemplate[]) {
return templates[Math.min(Math.max(Math.ceil((scene.gameMode.getWaveForDifficulty(scene.currentBattle?.waveIndex || startingWave, true) - 20) / 30), 0), templates.length - 1)]; return templates[Math.min(Math.max(Math.ceil((globalScene.gameMode.getWaveForDifficulty(globalScene.currentBattle?.waveIndex || startingWave, true) - 20) / 30), 0), templates.length - 1)];
} }
function getGymLeaderPartyTemplate(scene: BattleScene) { function getGymLeaderPartyTemplate() {
return getWavePartyTemplate(scene, trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5); return getWavePartyTemplate(trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5);
} }
/** /**
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength. * Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
* Then adds Pokemon to scene. * Then adds Pokemon to globalScene.
* @param speciesPool * @param speciesPool
* @param trainerSlot * @param trainerSlot
* @param ignoreEvolution * @param ignoreEvolution
* @param postProcess * @param postProcess
*/ */
export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) { export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => { return (level: number, strength: PartyMemberStrength) => {
let species = Utils.randSeedItem(speciesPool); let species = Utils.randSeedItem(speciesPool);
if (scene.gameMode.isClassic && scene.currentBattle.waveIndex === 20) { if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 20) {
ignoreEvolution = true; ignoreEvolution = true;
} }
if (!ignoreEvolution) { if (!ignoreEvolution) {
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex); species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex);
} }
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, false, undefined, postProcess); return globalScene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, false, undefined, postProcess);
}; };
} }
@ -1192,12 +1196,12 @@ function getSpeciesFilterRandomPartyMemberFunc(
return (allowLegendaries || notLegendary) && !species.isTrainerForbidden() && originalSpeciesFilter(species); return (allowLegendaries || notLegendary) && !species.isTrainerForbidden() && originalSpeciesFilter(species);
}; };
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => { return (level: number, strength: PartyMemberStrength) => {
const waveIndex = scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
const species = getPokemonSpecies(scene.randomSpecies(waveIndex, level, false, speciesFilter) const species = getPokemonSpecies(globalScene.randomSpecies(waveIndex, level, false, speciesFilter)
.getTrainerSpeciesForLevel(level, true, strength, waveIndex)); .getTrainerSpeciesForLevel(level, true, strength, waveIndex));
return scene.addEnemyPokemon(species, level, trainerSlot, undefined, false, undefined, postProcess); return globalScene.addEnemyPokemon(species, level, trainerSlot, undefined, false, undefined, postProcess);
}; };
} }
@ -1356,7 +1360,7 @@ export const signatureSpecies: SignatureSpecies = {
export const trainerConfigs: TrainerConfigs = { export const trainerConfigs: TrainerConfigs = {
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(), [TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders("Ace Trainer Female").setHasDouble("Ace Duo").setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER) [TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders("Ace Trainer Female").setHasDouble("Ace Duo").setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
.setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.THREE_WEAK_BALANCED, trainerPartyTemplates.FOUR_WEAK_BALANCED, trainerPartyTemplates.FIVE_WEAK_BALANCED, trainerPartyTemplates.SIX_WEAK_BALANCED)), .setPartyTemplateFunc(() => getWavePartyTemplate(trainerPartyTemplates.THREE_WEAK_BALANCED, trainerPartyTemplates.FOUR_WEAK_BALANCED, trainerPartyTemplates.FIVE_WEAK_BALANCED, trainerPartyTemplates.SIX_WEAK_BALANCED)),
[TrainerType.ARTIST]: new TrainerConfig(++t).setEncounterBgm(TrainerType.RICH).setPartyTemplates(trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.THREE_AVG) [TrainerType.ARTIST]: new TrainerConfig(++t).setEncounterBgm(TrainerType.RICH).setPartyTemplates(trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.THREE_AVG)
.setSpeciesPools([ Species.SMEARGLE ]), .setSpeciesPools([ Species.SMEARGLE ]),
[TrainerType.BACKERS]: new TrainerConfig(++t).setHasGenders("Backers").setDoubleOnly().setEncounterBgm(TrainerType.CYCLIST), [TrainerType.BACKERS]: new TrainerConfig(++t).setHasGenders("Backers").setDoubleOnly().setEncounterBgm(TrainerType.CYCLIST),
@ -1381,7 +1385,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerPoolTier.ULTRA_RARE]: [ Species.KUBFU ] [TrainerPoolTier.ULTRA_RARE]: [ Species.KUBFU ]
}), }),
[TrainerType.BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(1.325).setEncounterBgm(TrainerType.POKEFAN).setHasGenders("Breeder Female").setHasDouble("Breeders") [TrainerType.BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(1.325).setEncounterBgm(TrainerType.POKEFAN).setHasGenders("Breeder Female").setHasDouble("Breeders")
.setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.FIVE_WEAKER, trainerPartyTemplates.SIX_WEAKER)) .setPartyTemplateFunc(() => getWavePartyTemplate(trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.FIVE_WEAKER, trainerPartyTemplates.SIX_WEAKER))
.setSpeciesFilter(s => s.baseTotal < 450), .setSpeciesFilter(s => s.baseTotal < 450),
[TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders("Clerk Female").setHasDouble("Colleagues").setEncounterBgm(TrainerType.CLERK) [TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders("Clerk Female").setHasDouble("Colleagues").setEncounterBgm(TrainerType.CLERK)
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.THREE_WEAK, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.THREE_WEAK, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG)
@ -1500,7 +1504,7 @@ export const trainerConfigs: TrainerConfigs = {
}), }),
[TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders("Swimmer Female").setHasDouble("Swimmers").setSpecialtyTypes(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)), [TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders("Swimmer Female").setHasDouble("Swimmers").setSpecialtyTypes(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)),
[TrainerType.TWINS]: new TrainerConfig(++t).setDoubleOnly().setMoneyMultiplier(0.65).setUseSameSeedForAllMembers() [TrainerType.TWINS]: new TrainerConfig(++t).setDoubleOnly().setMoneyMultiplier(0.65).setUseSameSeedForAllMembers()
.setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_STRONG)) .setPartyTemplateFunc(() => getWavePartyTemplate(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_STRONG))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PLUSLE, Species.VOLBEAT, Species.PACHIRISU, Species.SILCOON, Species.METAPOD, Species.IGGLYBUFF, Species.PETILIL, Species.EEVEE ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PLUSLE, Species.VOLBEAT, Species.PACHIRISU, Species.SILCOON, Species.METAPOD, Species.IGGLYBUFF, Species.PETILIL, Species.EEVEE ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MINUN, Species.ILLUMISE, Species.EMOLGA, Species.CASCOON, Species.KAKUNA, Species.CLEFFA, Species.COTTONEE, Species.EEVEE ], TrainerSlot.TRAINER_PARTNER)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MINUN, Species.ILLUMISE, Species.EMOLGA, Species.CASCOON, Species.KAKUNA, Species.CLEFFA, Species.COTTONEE, Species.EEVEE ], TrainerSlot.TRAINER_PARTNER))
.setEncounterBgm(TrainerType.TWINS), .setEncounterBgm(TrainerType.TWINS),
@ -1516,115 +1520,115 @@ export const trainerConfigs: TrainerConfigs = {
.setSpeciesPools( .setSpeciesPools(
[ Species.CATERPIE, Species.WEEDLE, Species.RATTATA, Species.SENTRET, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.BIDOOF, Species.PATRAT, Species.LILLIPUP ] [ Species.CATERPIE, Species.WEEDLE, Species.RATTATA, Species.SENTRET, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.BIDOOF, Species.PATRAT, Species.LILLIPUP ]
), ),
[TrainerType.ROCKET_GRUNT]: new TrainerConfig(++t).setHasGenders("Rocket Grunt Female").setHasDouble("Rocket Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.ROCKET_GRUNT]: new TrainerConfig(++t).setHasGenders("Rocket Grunt Female").setHasDouble("Rocket Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH, Species.SLOWPOKE ], [TrainerPoolTier.COMMON]: [ Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH, Species.SLOWPOKE ],
[TrainerPoolTier.UNCOMMON]: [ Species.GYARADOS, Species.LICKITUNG, Species.TAUROS, Species.MANKEY, Species.SCYTHER, Species.ELEKID, Species.MAGBY, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.MAGNEMITE ], [TrainerPoolTier.UNCOMMON]: [ Species.GYARADOS, Species.LICKITUNG, Species.TAUROS, Species.MANKEY, Species.SCYTHER, Species.ELEKID, Species.MAGBY, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.MAGNEMITE ],
[TrainerPoolTier.RARE]: [ Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO ], [TrainerPoolTier.RARE]: [ Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DRATINI, Species.LARVITAR ] [TrainerPoolTier.SUPER_RARE]: [ Species.DRATINI, Species.LARVITAR ]
}), }),
[TrainerType.ARCHER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.HOUNDOOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.ARCHER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.HOUNDOOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.ARIANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin_female", "rocket", [ Species.ARBOK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.ARIANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin_female", "rocket", [ Species.ARBOK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.PROTON]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.CROBAT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.PROTON]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.CROBAT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.PETREL]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.WEEZING ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.PETREL]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [ Species.WEEZING ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MAGMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Magma Grunt Female").setHasDouble("Magma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.MAGMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Magma Grunt Female").setHasDouble("Magma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.GROWLITHE, Species.BALTOY ], [TrainerPoolTier.COMMON]: [ Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.GROWLITHE, Species.BALTOY ],
[TrainerPoolTier.UNCOMMON]: [ Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR, Species.RHYHORN, Species.HEATMOR ], [TrainerPoolTier.UNCOMMON]: [ Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR, Species.RHYHORN, Species.HEATMOR ],
[TrainerPoolTier.RARE]: [ Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ], [TrainerPoolTier.RARE]: [ Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ],
[TrainerPoolTier.SUPER_RARE]: [ Species.CAPSAKID, Species.CHARCADET ] [TrainerPoolTier.SUPER_RARE]: [ Species.CAPSAKID, Species.CHARCADET ]
}), }),
[TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.COURTNEY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin_female", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.COURTNEY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin_female", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.AQUA_GRUNT]: new TrainerConfig(++t).setHasGenders("Aqua Grunt Female").setHasDouble("Aqua Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.AQUA_GRUNT]: new TrainerConfig(++t).setHasGenders("Aqua Grunt Female").setHasDouble("Aqua Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH ], [TrainerPoolTier.COMMON]: [ Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH ],
[TrainerPoolTier.UNCOMMON]: [ Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA ], [TrainerPoolTier.UNCOMMON]: [ Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA ],
[TrainerPoolTier.RARE]: [ Species.MANTYKE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP ], [TrainerPoolTier.RARE]: [ Species.MANTYKE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.BASCULEGION ] [TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.BASCULEGION ]
}), }),
[TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SHELLY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin_female", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.SHELLY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin_female", "aqua", [ Species.SHARPEDO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.GALACTIC_GRUNT]: new TrainerConfig(++t).setHasGenders("Galactic Grunt Female").setHasDouble("Galactic Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.GALACTIC_GRUNT]: new TrainerConfig(++t).setHasGenders("Galactic Grunt Female").setHasDouble("Galactic Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY, Species.CARNIVINE ], [TrainerPoolTier.COMMON]: [ Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY, Species.CARNIVINE ],
[TrainerPoolTier.UNCOMMON]: [ Species.LICKITUNG, Species.RHYHORN, Species.TANGELA, Species.ZUBAT, Species.YANMA, Species.SKORUPI, Species.GLIGAR, Species.SWINUB ], [TrainerPoolTier.UNCOMMON]: [ Species.LICKITUNG, Species.RHYHORN, Species.TANGELA, Species.ZUBAT, Species.YANMA, Species.SKORUPI, Species.GLIGAR, Species.SWINUB ],
[TrainerPoolTier.RARE]: [ Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.ELEKID, Species.MAGBY, Species.DUSKULL ], [TrainerPoolTier.RARE]: [ Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.ELEKID, Species.MAGBY, Species.DUSKULL ],
[TrainerPoolTier.SUPER_RARE]: [ Species.ROTOM, Species.SPIRITOMB, Species.HISUI_SNEASEL ] [TrainerPoolTier.SUPER_RARE]: [ Species.ROTOM, Species.SPIRITOMB, Species.HISUI_SNEASEL ]
}), }),
[TrainerType.JUPITER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.SKUNTANK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.JUPITER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.SKUNTANK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MARS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.PURUGLY ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.MARS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [ Species.PURUGLY ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SATURN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander", "galactic", [ Species.TOXICROAK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.SATURN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander", "galactic", [ Species.TOXICROAK ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE ], [TrainerPoolTier.COMMON]: [ Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE ],
[TrainerPoolTier.UNCOMMON]: [ Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK, Species.CUBCHOO, Species.KLINK ], [TrainerPoolTier.UNCOMMON]: [ Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK, Species.CUBCHOO, Species.KLINK ],
[TrainerPoolTier.RARE]: [ Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT ], [TrainerPoolTier.RARE]: [ Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO ] [TrainerPoolTier.SUPER_RARE]: [ Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO ]
}), }),
[TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [ Species.CRYOGONAL ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [ Species.CRYOGONAL ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.ROOD]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [ Species.SWOOBAT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.ROOD]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [ Species.SWOOBAT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR ], [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR ],
[TrainerPoolTier.UNCOMMON]: [ Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP, Species.FOONGUS ], [TrainerPoolTier.UNCOMMON]: [ Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP, Species.FOONGUS ],
[TrainerPoolTier.RARE]: [ Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO ], [TrainerPoolTier.RARE]: [ Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO ],
[TrainerPoolTier.SUPER_RARE]: [ Species.NOIBAT, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG ] [TrainerPoolTier.SUPER_RARE]: [ Species.NOIBAT, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG ]
}), }),
[TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [ Species.LIEPARD ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [ Species.LIEPARD ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [ Species.MALAMAR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [ Species.MALAMAR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU ], [TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU ],
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK ], [TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK ],
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.TURTONATOR, Species.MIMIKYU, Species.MAGNEMITE, Species.DRAMPA ], [TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.TURTONATOR, Species.MIMIKYU, Species.MAGNEMITE, Species.DRAMPA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.JANGMO_O, Species.PORYGON ] [TrainerPoolTier.SUPER_RARE]: [ Species.JANGMO_O, Species.PORYGON ]
}), }),
[TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [ Species.HYPNO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [ Species.HYPNO ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH, Species.DROWZEE ], [TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH, Species.DROWZEE ],
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY ], [TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY ],
[TrainerPoolTier.RARE]: [ Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE ], [TrainerPoolTier.RARE]: [ Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE ],
[TrainerPoolTier.SUPER_RARE]: [ Species.GRUBBIN, Species.DEWPIDER ] [TrainerPoolTier.SUPER_RARE]: [ Species.GRUBBIN, Species.DEWPIDER ]
}), }),
[TrainerType.PLUMERIA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("skull_admin", "skull", [ Species.SALAZZLE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.PLUMERIA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("skull_admin", "skull", [ Species.SALAZZLE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.MACRO_GRUNT]: new TrainerConfig(++t).setHasGenders("Macro Grunt Female").setHasDouble("Macro Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_macro_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.MACRO_GRUNT]: new TrainerConfig(++t).setHasGenders("Macro Grunt Female").setHasDouble("Macro Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_macro_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.CUFANT, Species.GALAR_MEOWTH, Species.KLINK, Species.ROOKIDEE, Species.CRAMORANT, Species.GALAR_ZIGZAGOON, Species.SKWOVET, Species.STEELIX, Species.MAWILE, Species.FERROSEED ], [TrainerPoolTier.COMMON]: [ Species.CUFANT, Species.GALAR_MEOWTH, Species.KLINK, Species.ROOKIDEE, Species.CRAMORANT, Species.GALAR_ZIGZAGOON, Species.SKWOVET, Species.STEELIX, Species.MAWILE, Species.FERROSEED ],
[TrainerPoolTier.UNCOMMON]: [ Species.DRILBUR, Species.MAGNEMITE, Species.HATENNA, Species.ARROKUDA, Species.APPLIN, Species.GALAR_PONYTA, Species.GALAR_YAMASK, Species.SINISTEA, Species.RIOLU ], [TrainerPoolTier.UNCOMMON]: [ Species.DRILBUR, Species.MAGNEMITE, Species.HATENNA, Species.ARROKUDA, Species.APPLIN, Species.GALAR_PONYTA, Species.GALAR_YAMASK, Species.SINISTEA, Species.RIOLU ],
[TrainerPoolTier.RARE]: [ Species.FALINKS, Species.BELDUM, Species.GALAR_FARFETCHD, Species.GALAR_MR_MIME, Species.HONEDGE, Species.SCIZOR, Species.GALAR_DARUMAKA ], [TrainerPoolTier.RARE]: [ Species.FALINKS, Species.BELDUM, Species.GALAR_FARFETCHD, Species.GALAR_MR_MIME, Species.HONEDGE, Species.SCIZOR, Species.GALAR_DARUMAKA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DURALUDON, Species.DREEPY ] [TrainerPoolTier.SUPER_RARE]: [ Species.DURALUDON, Species.DREEPY ]
}), }),
[TrainerType.OLEANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("macro_admin", "macro", [ Species.GARBODOR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_oleana").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.OLEANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("macro_admin", "macro", [ Species.GARBODOR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_oleana").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()),
[TrainerType.STAR_GRUNT]: new TrainerConfig(++t).setHasGenders("Star Grunt Female").setHasDouble("Star Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.STAR_GRUNT]: new TrainerConfig(++t).setHasGenders("Star Grunt Female").setHasDouble("Star Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.DUNSPARCE, Species.HOUNDOUR, Species.AZURILL, Species.GULPIN, Species.FOONGUS, Species.FLETCHLING, Species.LITLEO, Species.FLABEBE, Species.CRABRAWLER, Species.NYMBLE, Species.PAWMI, Species.FIDOUGH, Species.SQUAWKABILLY, Species.MASCHIFF, Species.SHROODLE, Species.KLAWF, Species.WIGLETT, Species.PALDEA_WOOPER ], [TrainerPoolTier.COMMON]: [ Species.DUNSPARCE, Species.HOUNDOUR, Species.AZURILL, Species.GULPIN, Species.FOONGUS, Species.FLETCHLING, Species.LITLEO, Species.FLABEBE, Species.CRABRAWLER, Species.NYMBLE, Species.PAWMI, Species.FIDOUGH, Species.SQUAWKABILLY, Species.MASCHIFF, Species.SHROODLE, Species.KLAWF, Species.WIGLETT, Species.PALDEA_WOOPER ],
[TrainerPoolTier.UNCOMMON]: [ Species.KOFFING, Species.EEVEE, Species.GIRAFARIG, Species.RALTS, Species.TORKOAL, Species.SEVIPER, Species.SCRAGGY, Species.ZORUA, Species.MIMIKYU, Species.IMPIDIMP, Species.FALINKS, Species.CAPSAKID, Species.TINKATINK, Species.BOMBIRDIER, Species.CYCLIZAR, Species.FLAMIGO, Species.PALDEA_TAUROS ], [TrainerPoolTier.UNCOMMON]: [ Species.KOFFING, Species.EEVEE, Species.GIRAFARIG, Species.RALTS, Species.TORKOAL, Species.SEVIPER, Species.SCRAGGY, Species.ZORUA, Species.MIMIKYU, Species.IMPIDIMP, Species.FALINKS, Species.CAPSAKID, Species.TINKATINK, Species.BOMBIRDIER, Species.CYCLIZAR, Species.FLAMIGO, Species.PALDEA_TAUROS ],
[TrainerPoolTier.RARE]: [ Species.MANKEY, Species.PAWNIARD, Species.CHARCADET, Species.FLITTLE, Species.VAROOM, Species.ORTHWORM ], [TrainerPoolTier.RARE]: [ Species.MANKEY, Species.PAWNIARD, Species.CHARCADET, Species.FLITTLE, Species.VAROOM, Species.ORTHWORM ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.GIMMIGHOUL ] [TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.GIMMIGHOUL ]
}), }),
[TrainerType.GIACOMO]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_1", [ Species.KINGAMBIT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.GIACOMO]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_1", [ Species.KINGAMBIT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // Segin Starmobile p.formIndex = 1; // Segin Starmobile
p.moveset = [ new PokemonMove(Moves.WICKED_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.WICKED_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.MELA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_2", [ Species.ARMAROUGE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.MELA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_2", [ Species.ARMAROUGE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 2; // Schedar Starmobile p.formIndex = 2; // Schedar Starmobile
p.moveset = [ new PokemonMove(Moves.BLAZING_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.BLAZING_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ATTICUS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_3", [ Species.REVAVROOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.ATTICUS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_3", [ Species.REVAVROOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 3; // Navi Starmobile p.formIndex = 3; // Navi Starmobile
p.moveset = [ new PokemonMove(Moves.NOXIOUS_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.NOXIOUS_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ORTEGA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_4", [ Species.DACHSBUN ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.ORTEGA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_4", [ Species.DACHSBUN ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 4; // Ruchbah Starmobile p.formIndex = 4; // Ruchbah Starmobile
p.moveset = [ new PokemonMove(Moves.MAGICAL_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.MAGICAL_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ERI]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_5", [ Species.ANNIHILAPE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) [TrainerType.ERI]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_5", [ Species.ANNIHILAPE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 5; // Caph Starmobile p.formIndex = 5; // Caph Starmobile
p.moveset = [ new PokemonMove(Moves.COMBAT_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.COMBAT_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];

View File

@ -1,14 +1,16 @@
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import Move, { AttackMove } from "./move"; import type Move from "./move";
import { AttackMove } from "./move";
import * as Utils from "../utils"; import * as Utils from "../utils";
import BattleScene from "../battle-scene";
import { SuppressWeatherEffectAbAttr } from "./ability"; import { SuppressWeatherEffectAbAttr } from "./ability";
import { TerrainType, getTerrainName } from "./terrain"; import { TerrainType, getTerrainName } from "./terrain";
import i18next from "i18next"; import i18next from "i18next";
import { globalScene } from "#app/global-scene";
import type { Arena } from "#app/field/arena";
export class Weather { export class Weather {
public weatherType: WeatherType; public weatherType: WeatherType;
@ -100,8 +102,8 @@ export class Weather {
return false; return false;
} }
isEffectSuppressed(scene: BattleScene): boolean { isEffectSuppressed(): boolean {
const field = scene.getField(true); const field = globalScene.getField(true);
for (const pokemon of field) { for (const pokemon of field) {
let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0];
@ -247,7 +249,7 @@ export interface WeatherPoolEntry {
weight: integer; weight: integer;
} }
export function getRandomWeatherType(arena: any /* Importing from arena causes a circular dependency */): WeatherType { export function getRandomWeatherType(arena: Arena): WeatherType {
let weatherPool: WeatherPoolEntry[] = []; let weatherPool: WeatherPoolEntry[] = [];
const hasSun = arena.getTimeOfDay() < 2; const hasSun = arena.getTimeOfDay() < 2;
switch (arena.biomeType) { switch (arena.biomeType) {
@ -373,8 +375,8 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a
break; break;
} }
if (arena.biomeType === Biome.TOWN && arena.scene.eventManager.isEventActive() && arena.scene.eventManager.activeEvent()?.weather?.length > 0) { if (arena.biomeType === Biome.TOWN && globalScene.eventManager.isEventActive() && (globalScene.eventManager.activeEvent()?.weather?.length ?? 0) > 0) {
arena.scene.eventManager.activeEvent().weather.map(w => weatherPool.push(w)); globalScene.eventManager.activeEvent()?.weather?.map(w => weatherPool.push(w));
} }
if (weatherPool.length > 1) { if (weatherPool.length > 1) {

View File

@ -1,7 +1,7 @@
import { ArenaTagSide } from "#app/data/arena-tag"; import type { ArenaTagSide } from "#app/data/arena-tag";
import { ArenaTagType } from "#enums/arena-tag-type"; import type { ArenaTagType } from "#enums/arena-tag-type";
import { TerrainType } from "#app/data/terrain"; import type { TerrainType } from "#app/data/terrain";
import { WeatherType } from "#enums/weather-type"; import type { WeatherType } from "#enums/weather-type";
/** Alias for all {@linkcode ArenaEvent} type strings */ /** Alias for all {@linkcode ArenaEvent} type strings */
export enum ArenaEventType { export enum ArenaEventType {

View File

@ -1,5 +1,5 @@
import Move from "../data/move"; import type Move from "../data/move";
import { BerryModifier } from "../modifier/modifier"; import type { BerryModifier } from "../modifier/modifier";
/** Alias for all {@linkcode BattleScene} events */ /** Alias for all {@linkcode BattleScene} events */
export enum BattleSceneEventType { export enum BattleSceneEventType {

View File

@ -1,32 +1,32 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { Variant } from "#app/data/variant"; import type { Variant } from "#app/data/variant";
import { getFrameMs, randGauss } from "#app/utils"; import { getFrameMs, randGauss } from "#app/utils";
export function addPokeballOpenParticles(scene: BattleScene, x: number, y: number, pokeballType: PokeballType): void { export function addPokeballOpenParticles(x: number, y: number, pokeballType: PokeballType): void {
switch (pokeballType) { switch (pokeballType) {
case PokeballType.POKEBALL: case PokeballType.POKEBALL:
doDefaultPbOpenParticles(scene, x, y, 48); doDefaultPbOpenParticles(x, y, 48);
break; break;
case PokeballType.GREAT_BALL: case PokeballType.GREAT_BALL:
doDefaultPbOpenParticles(scene, x, y, 96); doDefaultPbOpenParticles(x, y, 96);
break; break;
case PokeballType.ULTRA_BALL: case PokeballType.ULTRA_BALL:
doUbOpenParticles(scene, x, y, 8); doUbOpenParticles(x, y, 8);
break; break;
case PokeballType.ROGUE_BALL: case PokeballType.ROGUE_BALL:
doUbOpenParticles(scene, x, y, 10); doUbOpenParticles(x, y, 10);
break; break;
case PokeballType.MASTER_BALL: case PokeballType.MASTER_BALL:
doMbOpenParticles(scene, x, y); doMbOpenParticles(x, y);
break; break;
} }
} }
function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radius: number) { function doDefaultPbOpenParticles(x: number, y: number, radius: number) {
const pbOpenParticlesFrameNames = scene.anims.generateFrameNames("pb_particles", { start: 0, end: 3, suffix: ".png" }); const pbOpenParticlesFrameNames = globalScene.anims.generateFrameNames("pb_particles", { start: 0, end: 3, suffix: ".png" });
if (!(scene.anims.exists("pb_open_particle"))) { if (!(globalScene.anims.exists("pb_open_particle"))) {
scene.anims.create({ globalScene.anims.create({
key: "pb_open_particle", key: "pb_open_particle",
frames: pbOpenParticlesFrameNames, frames: pbOpenParticlesFrameNames,
frameRate: 16, frameRate: 16,
@ -35,11 +35,11 @@ function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radi
} }
const addParticle = (index: integer) => { const addParticle = (index: integer) => {
const particle = scene.add.sprite(x, y, "pb_open_particle"); const particle = globalScene.add.sprite(x, y, "pb_open_particle");
scene.field.add(particle); globalScene.field.add(particle);
const angle = index * 45; const angle = index * 45;
const [ xCoord, yCoord ] = [ radius * Math.cos(angle * Math.PI / 180), radius * Math.sin(angle * Math.PI / 180) ]; const [ xCoord, yCoord ] = [ radius * Math.cos(angle * Math.PI / 180), radius * Math.sin(angle * Math.PI / 180) ];
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
x: x + xCoord, x: x + xCoord,
y: y + yCoord, y: y + yCoord,
@ -48,9 +48,9 @@ function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radi
particle.play({ particle.play({
key: "pb_open_particle", key: "pb_open_particle",
startFrame: (index + 3) % 4, startFrame: (index + 3) % 4,
frameRate: Math.floor(16 * scene.gameSpeed) frameRate: Math.floor(16 * globalScene.gameSpeed)
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
delay: 500, delay: 500,
duration: 75, duration: 75,
@ -61,20 +61,20 @@ function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radi
}; };
let particleCount = 0; let particleCount = 0;
scene.time.addEvent({ globalScene.time.addEvent({
delay: 20, delay: 20,
repeat: 16, repeat: 16,
callback: () => addParticle(++particleCount) callback: () => addParticle(++particleCount)
}); });
} }
function doUbOpenParticles(scene: BattleScene, x: number, y: number, frameIndex: integer) { function doUbOpenParticles(x: number, y: number, frameIndex: integer) {
const particles: Phaser.GameObjects.Image[] = []; const particles: Phaser.GameObjects.Image[] = [];
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
particles.push(doFanOutParticle(scene, i * 25, x, y, 1, 1, 5, frameIndex)); particles.push(doFanOutParticle(i * 25, x, y, 1, 1, 5, frameIndex));
} }
scene.tweens.add({ globalScene.tweens.add({
targets: particles, targets: particles,
delay: 750, delay: 750,
duration: 250, duration: 250,
@ -88,14 +88,14 @@ function doUbOpenParticles(scene: BattleScene, x: number, y: number, frameIndex:
}); });
} }
function doMbOpenParticles(scene: BattleScene, x: number, y: number) { function doMbOpenParticles(x: number, y: number) {
const particles: Phaser.GameObjects.Image[] = []; const particles: Phaser.GameObjects.Image[] = [];
for (let j = 0; j < 2; j++) { for (let j = 0; j < 2; j++) {
for (let i = 0; i < 8; i++) { for (let i = 0; i < 8; i++) {
particles.push(doFanOutParticle(scene, i * 32, x, y, j ? 1 : 2, j ? 2 : 1, 8, 4)); particles.push(doFanOutParticle(i * 32, x, y, j ? 1 : 2, j ? 2 : 1, 8, 4));
} }
scene.tweens.add({ globalScene.tweens.add({
targets: particles, targets: particles,
delay: 750, delay: 750,
duration: 250, duration: 250,
@ -110,11 +110,11 @@ function doMbOpenParticles(scene: BattleScene, x: number, y: number) {
} }
} }
function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y: integer, xSpeed: integer, ySpeed: integer, angle: integer, frameIndex: integer): Phaser.GameObjects.Image { function doFanOutParticle(trigIndex: integer, x: integer, y: integer, xSpeed: integer, ySpeed: integer, angle: integer, frameIndex: integer): Phaser.GameObjects.Image {
let f = 0; let f = 0;
const particle = scene.add.image(x, y, "pb_particles", `${frameIndex}.png`); const particle = globalScene.add.image(x, y, "pb_particles", `${frameIndex}.png`);
scene.field.add(particle); globalScene.field.add(particle);
const updateParticle = () => { const updateParticle = () => {
if (!particle.scene) { if (!particle.scene) {
@ -126,7 +126,7 @@ function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y:
f++; f++;
}; };
const particleTimer = scene.tweens.addCounter({ const particleTimer = globalScene.tweens.addCounter({
repeat: -1, repeat: -1,
duration: getFrameMs(1), duration: getFrameMs(1),
onRepeat: () => { onRepeat: () => {
@ -137,20 +137,20 @@ function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y:
return particle; return particle;
} }
export function addPokeballCaptureStars(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite): void { export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): void {
const addParticle = () => { const addParticle = () => {
const particle = scene.add.sprite(pokeball.x, pokeball.y, "pb_particles", "4.png"); const particle = globalScene.add.sprite(pokeball.x, pokeball.y, "pb_particles", "4.png");
particle.setOrigin(pokeball.originX, pokeball.originY); particle.setOrigin(pokeball.originX, pokeball.originY);
particle.setAlpha(0.5); particle.setAlpha(0.5);
scene.field.add(particle); globalScene.field.add(particle);
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
y: pokeball.y - 10, y: pokeball.y - 10,
ease: "Sine.easeOut", ease: "Sine.easeOut",
duration: 250, duration: 250,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
y: pokeball.y, y: pokeball.y,
alpha: 0, alpha: 0,
@ -161,13 +161,13 @@ export function addPokeballCaptureStars(scene: BattleScene, pokeball: Phaser.Gam
}); });
const dist = randGauss(25); const dist = randGauss(25);
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
x: pokeball.x + dist, x: pokeball.x + dist,
duration: 500 duration: 500
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: particle, targets: particle,
alpha: 0, alpha: 0,
delay: 425, delay: 425,
@ -193,15 +193,15 @@ export function cos(index: integer, amplitude: integer): number {
* @param sparkleSprite the Sprite to play the animation on * @param sparkleSprite the Sprite to play the animation on
* @param variant which shiny {@linkcode variant} to play the animation for * @param variant which shiny {@linkcode variant} to play the animation for
*/ */
export function doShinySparkleAnim(scene: BattleScene, sparkleSprite: Phaser.GameObjects.Sprite, variant: Variant) { export function doShinySparkleAnim(sparkleSprite: Phaser.GameObjects.Sprite, variant: Variant) {
const keySuffix = variant ? `_${variant + 1}` : ""; const keySuffix = variant ? `_${variant + 1}` : "";
const spriteKey = `shiny${keySuffix}`; const spriteKey = `shiny${keySuffix}`;
const animationKey = `sparkle${keySuffix}`; const animationKey = `sparkle${keySuffix}`;
// Make sure the animation exists, and create it if not // Make sure the animation exists, and create it if not
if (!scene.anims.exists(animationKey)) { if (!globalScene.anims.exists(animationKey)) {
const frameNames = scene.anims.generateFrameNames(spriteKey, { suffix: ".png", end: 34 }); const frameNames = globalScene.anims.generateFrameNames(spriteKey, { suffix: ".png", end: 34 });
scene.anims.create({ globalScene.anims.create({
key: `sparkle${keySuffix}`, key: `sparkle${keySuffix}`,
frames: frameNames, frames: frameNames,
frameRate: 32, frameRate: 32,
@ -212,5 +212,5 @@ export function doShinySparkleAnim(scene: BattleScene, sparkleSprite: Phaser.Gam
// Play the animation // Play the animation
sparkleSprite.play(animationKey); sparkleSprite.play(animationKey);
scene.playSound("se/sparkle"); globalScene.playSound("se/sparkle");
} }

View File

@ -1,14 +1,17 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { biomePokemonPools, BiomePoolTier, BiomeTierTrainerPools, biomeTrainerPools, PokemonPools } from "#app/data/balance/biomes"; import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biomes";
import { Constructor } from "#app/utils"; import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes";
import type { Constructor } from "#app/utils";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage, Weather } from "#app/data/weather"; import { getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage, Weather } from "#app/data/weather";
import { CommonAnim } from "#app/data/battle-anims"; import { CommonAnim } from "#app/data/battle-anims";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import Move from "#app/data/move"; import type Move from "#app/data/move";
import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "#app/data/arena-tag"; import type { ArenaTag } from "#app/data/arena-tag";
import { BattlerIndex } from "#app/battle"; import { ArenaTagSide, ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
import type { BattlerIndex } from "#app/battle";
import { Terrain, TerrainType } from "#app/data/terrain"; import { Terrain, TerrainType } from "#app/data/terrain";
import { import {
applyAbAttrs, applyAbAttrs,
@ -18,12 +21,12 @@ import {
PostWeatherChangeAbAttr, PostWeatherChangeAbAttr,
TerrainEventTypeChangeAbAttr TerrainEventTypeChangeAbAttr
} from "#app/data/ability"; } from "#app/data/ability";
import Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
import { ArenaTagType } from "#enums/arena-tag-type"; import type { ArenaTagType } from "#enums/arena-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
@ -34,7 +37,6 @@ import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
export class Arena { export class Arena {
public scene: BattleScene;
public biomeType: Biome; public biomeType: Biome;
public weather: Weather | null; public weather: Weather | null;
public terrain: Terrain | null; public terrain: Terrain | null;
@ -50,8 +52,7 @@ export class Arena {
public readonly eventTarget: EventTarget = new EventTarget(); public readonly eventTarget: EventTarget = new EventTarget();
constructor(scene: BattleScene, biome: Biome, bgm: string) { constructor(biome: Biome, bgm: string) {
this.scene = scene;
this.biomeType = biome; this.biomeType = biome;
this.tags = []; this.tags = [];
this.bgm = bgm; this.bgm = bgm;
@ -62,12 +63,12 @@ export class Arena {
init() { init() {
const biomeKey = getBiomeKey(this.biomeType); const biomeKey = getBiomeKey(this.biomeType);
this.scene.arenaPlayer.setBiome(this.biomeType); globalScene.arenaPlayer.setBiome(this.biomeType);
this.scene.arenaPlayerTransition.setBiome(this.biomeType); globalScene.arenaPlayerTransition.setBiome(this.biomeType);
this.scene.arenaEnemy.setBiome(this.biomeType); globalScene.arenaEnemy.setBiome(this.biomeType);
this.scene.arenaNextEnemy.setBiome(this.biomeType); globalScene.arenaNextEnemy.setBiome(this.biomeType);
this.scene.arenaBg.setTexture(`${biomeKey}_bg`); globalScene.arenaBg.setTexture(`${biomeKey}_bg`);
this.scene.arenaBgTransition.setTexture(`${biomeKey}_bg`); globalScene.arenaBgTransition.setTexture(`${biomeKey}_bg`);
// Redo this on initialize because during save/load the current wave isn't always // Redo this on initialize because during save/load the current wave isn't always
// set correctly during construction // set correctly during construction
@ -86,12 +87,12 @@ export class Arena {
} }
randomSpecies(waveIndex: integer, level: integer, attempt?: integer, luckValue?: integer, isBoss?: boolean): PokemonSpecies { randomSpecies(waveIndex: integer, level: integer, attempt?: integer, luckValue?: integer, isBoss?: boolean): PokemonSpecies {
const overrideSpecies = this.scene.gameMode.getOverrideSpecies(waveIndex); const overrideSpecies = globalScene.gameMode.getOverrideSpecies(waveIndex);
if (overrideSpecies) { if (overrideSpecies) {
return overrideSpecies; return overrideSpecies;
} }
const isBossSpecies = !!this.scene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length const isBossSpecies = !!globalScene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length
&& (this.biomeType !== Biome.END || this.scene.gameMode.isClassic || this.scene.gameMode.isWaveFinal(waveIndex)); && (this.biomeType !== Biome.END || globalScene.gameMode.isClassic || globalScene.gameMode.isWaveFinal(waveIndex));
const randVal = isBossSpecies ? 64 : 512; const randVal = isBossSpecies ? 64 : 512;
// luck influences encounter rarity // luck influences encounter rarity
let luckModifier = 0; let luckModifier = 0;
@ -111,7 +112,7 @@ export class Arena {
let ret: PokemonSpecies; let ret: PokemonSpecies;
let regen = false; let regen = false;
if (!tierPool.length) { if (!tierPool.length) {
ret = this.scene.randomSpecies(waveIndex, level); ret = globalScene.randomSpecies(waveIndex, level);
} else { } else {
const entry = tierPool[Utils.randSeedInt(tierPool.length)]; const entry = tierPool[Utils.randSeedInt(tierPool.length)];
let species: Species; let species: Species;
@ -158,7 +159,7 @@ export class Arena {
return this.randomSpecies(waveIndex, level, (attempt || 0) + 1); return this.randomSpecies(waveIndex, level, (attempt || 0) + 1);
} }
const newSpeciesId = ret.getWildSpeciesForLevel(level, true, isBoss ?? isBossSpecies, this.scene.gameMode); const newSpeciesId = ret.getWildSpeciesForLevel(level, true, isBoss ?? isBossSpecies, globalScene.gameMode);
if (newSpeciesId !== ret.speciesId) { if (newSpeciesId !== ret.speciesId) {
console.log("Replaced", Species[ret.speciesId], "with", Species[newSpeciesId]); console.log("Replaced", Species[ret.speciesId], "with", Species[newSpeciesId]);
ret = getPokemonSpecies(newSpeciesId); ret = getPokemonSpecies(newSpeciesId);
@ -168,7 +169,7 @@ export class Arena {
randomTrainerType(waveIndex: integer, isBoss: boolean = false): TrainerType { randomTrainerType(waveIndex: integer, isBoss: boolean = false): TrainerType {
const isTrainerBoss = !!this.trainerPool[BiomePoolTier.BOSS].length const isTrainerBoss = !!this.trainerPool[BiomePoolTier.BOSS].length
&& (this.scene.gameMode.isTrainerBoss(waveIndex, this.biomeType, this.scene.offsetGym) || isBoss); && (globalScene.gameMode.isTrainerBoss(waveIndex, this.biomeType, globalScene.offsetGym) || isBoss);
console.log(isBoss, this.trainerPool); console.log(isBoss, this.trainerPool);
const tierValue = Utils.randSeedInt(!isTrainerBoss ? 512 : 64); const tierValue = Utils.randSeedInt(!isTrainerBoss ? 512 : 64);
let tier = !isTrainerBoss let tier = !isTrainerBoss
@ -243,8 +244,8 @@ export class Arena {
*/ */
trySetWeatherOverride(weather: WeatherType): boolean { trySetWeatherOverride(weather: WeatherType): boolean {
this.weather = new Weather(weather, 0); this.weather = new Weather(weather, 0);
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
return true; return true;
} }
@ -269,13 +270,13 @@ export class Arena {
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!)); // TODO: is this bang correct? this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!)); // TODO: is this bang correct?
if (this.weather) { if (this.weather) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1), true)); globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1), true));
this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct?
} else { } else {
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct? globalScene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct?
} }
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { globalScene.getField(true).filter(p => p.isOnField()).map(pokemon => {
pokemon.findAndRemoveTags(t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather)); pokemon.findAndRemoveTags(t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather));
applyPostWeatherChangeAbAttrs(PostWeatherChangeAbAttr, pokemon, weather); applyPostWeatherChangeAbAttrs(PostWeatherChangeAbAttr, pokemon, weather);
}); });
@ -287,13 +288,13 @@ export class Arena {
* Function to trigger all weather based form changes * Function to trigger all weather based form changes
*/ */
triggerWeatherBasedFormChanges(): void { triggerWeatherBasedFormChanges(): void {
this.scene.getField(true).forEach( p => { globalScene.getField(true).forEach( p => {
const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM); const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST) && p.species.speciesId === Species.CASTFORM);
const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM); const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM);
if (isCastformWithForecast || isCherrimWithFlowerGift) { if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(this.scene, p.getBattlerIndex()); new ShowAbilityPhase(p.getBattlerIndex());
this.scene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger); globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
} }
}); });
} }
@ -302,13 +303,13 @@ export class Arena {
* Function to trigger all weather based form changes back into their normal forms * Function to trigger all weather based form changes back into their normal forms
*/ */
triggerWeatherBasedFormChangesToNormal(): void { triggerWeatherBasedFormChangesToNormal(): void {
this.scene.getField(true).forEach( p => { globalScene.getField(true).forEach( p => {
const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM); const isCastformWithForecast = (p.hasAbility(Abilities.FORECAST, false, true) && p.species.speciesId === Species.CASTFORM);
const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM); const isCherrimWithFlowerGift = (p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM);
if (isCastformWithForecast || isCherrimWithFlowerGift) { if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(this.scene, p.getBattlerIndex()); new ShowAbilityPhase(p.getBattlerIndex());
return this.scene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger); return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
} }
}); });
} }
@ -325,14 +326,14 @@ export class Arena {
if (this.terrain) { if (this.terrain) {
if (!ignoreAnim) { if (!ignoreAnim) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1))); globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1)));
} }
this.scene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? globalScene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct?
} else { } else {
this.scene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? globalScene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct?
} }
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { globalScene.getField(true).filter(p => p.isOnField()).map(pokemon => {
pokemon.findAndRemoveTags(t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain)); pokemon.findAndRemoveTags(t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain));
applyPostTerrainChangeAbAttrs(PostTerrainChangeAbAttr, pokemon, terrain); applyPostTerrainChangeAbAttrs(PostTerrainChangeAbAttr, pokemon, terrain);
applyAbAttrs(TerrainEventTypeChangeAbAttr, pokemon, null, false); applyAbAttrs(TerrainEventTypeChangeAbAttr, pokemon, null, false);
@ -342,7 +343,7 @@ export class Arena {
} }
public isMoveWeatherCancelled(user: Pokemon, move: Move): boolean { public isMoveWeatherCancelled(user: Pokemon, move: Move): boolean {
return !!this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move); return !!this.weather && !this.weather.isEffectSuppressed() && this.weather.isMoveWeatherCancelled(user, move);
} }
public isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean { public isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
@ -355,7 +356,7 @@ export class Arena {
getAttackTypeMultiplier(attackType: Type, grounded: boolean): number { getAttackTypeMultiplier(attackType: Type, grounded: boolean): number {
let weatherMultiplier = 1; let weatherMultiplier = 1;
if (this.weather && !this.weather.isEffectSuppressed(this.scene)) { if (this.weather && !this.weather.isEffectSuppressed()) {
weatherMultiplier = this.weather.getAttackTypeMultiplier(attackType); weatherMultiplier = this.weather.getAttackTypeMultiplier(attackType);
} }
@ -421,7 +422,7 @@ export class Arena {
return TimeOfDay.NIGHT; return TimeOfDay.NIGHT;
} }
const waveCycle = ((this.scene.currentBattle?.waveIndex || 0) + this.scene.waveCycleOffset) % 40; const waveCycle = ((globalScene.currentBattle?.waveIndex || 0) + globalScene.waveCycleOffset) % 40;
if (waveCycle < 15) { if (waveCycle < 15) {
return TimeOfDay.DAY; return TimeOfDay.DAY;
@ -691,7 +692,7 @@ export class Arena {
} }
preloadBgm(): void { preloadBgm(): void {
this.scene.loadBgm(this.bgm); globalScene.loadBgm(this.bgm);
} }
getBgmLoopPoint(): number { getBgmLoopPoint(): number {
@ -815,17 +816,17 @@ export class ArenaBase extends Phaser.GameObjects.Container {
public base: Phaser.GameObjects.Sprite; public base: Phaser.GameObjects.Sprite;
public props: Phaser.GameObjects.Sprite[]; public props: Phaser.GameObjects.Sprite[];
constructor(scene: BattleScene, player: boolean) { constructor(player: boolean) {
super(scene, 0, 0); super(globalScene, 0, 0);
this.player = player; this.player = player;
this.base = scene.addFieldSprite(0, 0, "plains_a", undefined, 1); this.base = globalScene.addFieldSprite(0, 0, "plains_a", undefined, 1);
this.base.setOrigin(0, 0); this.base.setOrigin(0, 0);
this.props = !player ? this.props = !player ?
new Array(3).fill(null).map(() => { new Array(3).fill(null).map(() => {
const ret = scene.addFieldSprite(0, 0, "plains_b", undefined, 1); const ret = globalScene.addFieldSprite(0, 0, "plains_b", undefined, 1);
ret.setOrigin(0, 0); ret.setOrigin(0, 0);
ret.setVisible(false); ret.setVisible(false);
return ret; return ret;
@ -841,9 +842,9 @@ export class ArenaBase extends Phaser.GameObjects.Container {
this.base.setTexture(baseKey); this.base.setTexture(baseKey);
if (this.base.texture.frameTotal > 1) { if (this.base.texture.frameTotal > 1) {
const baseFrameNames = this.scene.anims.generateFrameNames(baseKey, { zeroPad: 4, suffix: ".png", start: 1, end: this.base.texture.frameTotal - 1 }); const baseFrameNames = globalScene.anims.generateFrameNames(baseKey, { zeroPad: 4, suffix: ".png", start: 1, end: this.base.texture.frameTotal - 1 });
if (!(this.scene.anims.exists(baseKey))) { if (!(globalScene.anims.exists(baseKey))) {
this.scene.anims.create({ globalScene.anims.create({
key: baseKey, key: baseKey,
frames: baseFrameNames, frames: baseFrameNames,
frameRate: 12, frameRate: 12,
@ -859,7 +860,7 @@ export class ArenaBase extends Phaser.GameObjects.Container {
} }
if (!this.player) { if (!this.player) {
(this.scene as BattleScene).executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
this.propValue = propValue === undefined this.propValue = propValue === undefined
? hasProps ? Utils.randSeedInt(8) : 0 ? hasProps ? Utils.randSeedInt(8) : 0
: propValue; : propValue;
@ -868,9 +869,9 @@ export class ArenaBase extends Phaser.GameObjects.Container {
prop.setTexture(propKey); prop.setTexture(propKey);
if (hasProps && prop.texture.frameTotal > 1) { if (hasProps && prop.texture.frameTotal > 1) {
const propFrameNames = this.scene.anims.generateFrameNames(propKey, { zeroPad: 4, suffix: ".png", start: 1, end: prop.texture.frameTotal - 1 }); const propFrameNames = globalScene.anims.generateFrameNames(propKey, { zeroPad: 4, suffix: ".png", start: 1, end: prop.texture.frameTotal - 1 });
if (!(this.scene.anims.exists(propKey))) { if (!(globalScene.anims.exists(propKey))) {
this.scene.anims.create({ globalScene.anims.create({
key: propKey, key: propKey,
frames: propFrameNames, frames: propFrameNames,
frameRate: 12, frameRate: 12,
@ -885,7 +886,7 @@ export class ArenaBase extends Phaser.GameObjects.Container {
prop.setVisible(hasProps && !!(this.propValue & (1 << p))); prop.setVisible(hasProps && !!(this.propValue & (1 << p)));
this.add(prop); this.add(prop);
}); });
}, (this.scene as BattleScene).currentBattle?.waveIndex || 0, (this.scene as BattleScene).waveSeed); }, globalScene.currentBattle?.waveIndex || 0, globalScene.waveSeed);
} }
} }
} }

View File

@ -1,7 +1,10 @@
import { TextStyle, addTextObject } from "../ui/text"; import { TextStyle, addTextObject } from "../ui/text";
import Pokemon, { DamageResult, HitResult } from "./pokemon"; import type { DamageResult } from "./pokemon";
import type Pokemon from "./pokemon";
import { HitResult } from "./pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import type { BattlerIndex } from "../battle";
import { globalScene } from "#app/global-scene";
type TextAndShadowArr = [ string | null, string | null ]; type TextAndShadowArr = [ string | null, string | null ];
@ -13,15 +16,13 @@ export default class DamageNumberHandler {
} }
add(target: Pokemon, amount: integer, result: DamageResult | HitResult.HEAL = HitResult.EFFECTIVE, critical: boolean = false): void { add(target: Pokemon, amount: integer, result: DamageResult | HitResult.HEAL = HitResult.EFFECTIVE, critical: boolean = false): void {
const scene = target.scene; if (!globalScene?.damageNumbersMode) {
if (!scene?.damageNumbersMode) {
return; return;
} }
const battlerIndex = target.getBattlerIndex(); const battlerIndex = target.getBattlerIndex();
const baseScale = target.getSpriteScale() / 6; const baseScale = target.getSpriteScale() / 6;
const damageNumber = addTextObject(scene, target.x, -(scene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, Utils.formatStat(amount, true), TextStyle.SUMMARY); const damageNumber = addTextObject(target.x, -(globalScene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, Utils.formatStat(amount, true), TextStyle.SUMMARY);
damageNumber.setName("text-damage-number"); damageNumber.setName("text-damage-number");
damageNumber.setOrigin(0.5, 1); damageNumber.setOrigin(0.5, 1);
damageNumber.setScale(baseScale); damageNumber.setScale(baseScale);
@ -58,7 +59,7 @@ export default class DamageNumberHandler {
} }
} }
scene.fieldUI.add(damageNumber); globalScene.fieldUI.add(damageNumber);
if (!this.damageNumbers.has(battlerIndex)) { if (!this.damageNumbers.has(battlerIndex)) {
this.damageNumbers.set(battlerIndex, []); this.damageNumbers.set(battlerIndex, []);
@ -71,14 +72,14 @@ export default class DamageNumberHandler {
this.damageNumbers.get(battlerIndex)!.push(damageNumber); this.damageNumbers.get(battlerIndex)!.push(damageNumber);
if (scene.damageNumbersMode === 1) { if (globalScene.damageNumbersMode === 1) {
scene.tweens.add({ globalScene.tweens.add({
targets: damageNumber, targets: damageNumber,
duration: Utils.fixedInt(750), duration: Utils.fixedInt(750),
alpha: 1, alpha: 1,
y: "-=32" y: "-=32"
}); });
scene.tweens.add({ globalScene.tweens.add({
delay: 375, delay: 375,
targets: damageNumber, targets: damageNumber,
duration: Utils.fixedInt(625), duration: Utils.fixedInt(625),
@ -94,7 +95,7 @@ export default class DamageNumberHandler {
damageNumber.setAlpha(0); damageNumber.setAlpha(0);
scene.tweens.chain({ globalScene.tweens.chain({
targets: damageNumber, targets: damageNumber,
tweens: [ tweens: [
{ {

View File

@ -1,12 +1,12 @@
import { GameObjects } from "phaser"; import type { GameObjects } from "phaser";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { Species } from "#enums/species"; import type { Species } from "#enums/species";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; import type { Variant } from "#app/data/variant";
import { Variant } from "#app/data/variant";
import { doShinySparkleAnim } from "#app/field/anims"; import { doShinySparkleAnim } from "#app/field/anims";
import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig;
type KnownFileRoot = type KnownFileRoot =
| "arenas" | "arenas"
@ -82,8 +82,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
public enterFromRight: boolean; public enterFromRight: boolean;
private shinySparkleSprites: { sprite: Phaser.GameObjects.Sprite, variant: Variant }[]; private shinySparkleSprites: { sprite: Phaser.GameObjects.Sprite, variant: Variant }[];
constructor(scene: BattleScene, encounter: MysteryEncounter) { constructor(encounter: MysteryEncounter) {
super(scene, -72, 76); super(globalScene, -72, 76);
this.encounter = encounter; this.encounter = encounter;
this.enterFromRight = encounter.enterIntroVisualsFromRight ?? false; this.enterFromRight = encounter.enterIntroVisualsFromRight ?? false;
// Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter // Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter
@ -106,16 +106,16 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
} }
const getSprite = (spriteKey: string, hasShadow?: boolean, yShadow?: number) => { const getSprite = (spriteKey: string, hasShadow?: boolean, yShadow?: number) => {
const ret = this.scene.addFieldSprite(0, 0, spriteKey); const ret = globalScene.addFieldSprite(0, 0, spriteKey);
ret.setOrigin(0.5, 1); ret.setOrigin(0.5, 1);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, yShadowOffset: yShadow ?? 0 }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, yShadowOffset: yShadow ?? 0 });
return ret; return ret;
}; };
const getItemSprite = (spriteKey: string, hasShadow?: boolean, yShadow?: number) => { const getItemSprite = (spriteKey: string, hasShadow?: boolean, yShadow?: number) => {
const icon = this.scene.add.sprite(-19, 2, "items", spriteKey); const icon = globalScene.add.sprite(-19, 2, "items", spriteKey);
icon.setOrigin(0.5, 1); icon.setOrigin(0.5, 1);
icon.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, yShadowOffset: yShadow ?? 0 }); icon.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow, yShadowOffset: yShadow ?? 0 });
return icon; return icon;
}; };
@ -128,7 +128,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1)); const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1));
this.shinySparkleSprites = []; this.shinySparkleSprites = [];
const shinySparkleSprites = scene.add.container(0, 0); const shinySparkleSprites = globalScene.add.container(0, 0);
this.spriteConfigs?.forEach((config) => { this.spriteConfigs?.forEach((config) => {
const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha, isPokemon, isShiny, variant } = config; const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha, isPokemon, isShiny, variant } = config;
@ -151,7 +151,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
tintSprite.setPipelineData("shiny", true); tintSprite.setPipelineData("shiny", true);
tintSprite.setPipelineData("variant", variant); tintSprite.setPipelineData("variant", variant);
// Create Sprite for shiny Sparkle // Create Sprite for shiny Sparkle
pokemonShinySparkle = scene.add.sprite(sprite.x, sprite.y, "shiny"); pokemonShinySparkle = globalScene.add.sprite(sprite.x, sprite.y, "shiny");
pokemonShinySparkle.setOrigin(0.5, 1); pokemonShinySparkle.setOrigin(0.5, 1);
pokemonShinySparkle.setVisible(false); pokemonShinySparkle.setVisible(false);
this.shinySparkleSprites.push({ sprite: pokemonShinySparkle, variant: variant ?? 0 }); this.shinySparkleSprites.push({ sprite: pokemonShinySparkle, variant: variant ?? 0 });
@ -217,18 +217,18 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
this.spriteConfigs.forEach((config) => { this.spriteConfigs.forEach((config) => {
if (config.isPokemon) { if (config.isPokemon) {
this.scene.loadPokemonAtlas(config.spriteKey, config.fileRoot); globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
if (config.isShiny) { if (config.isShiny) {
this.scene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant); globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant);
} }
} else if (config.isItem) { } else if (config.isItem) {
this.scene.loadAtlas("items", ""); globalScene.loadAtlas("items", "");
} else { } else {
this.scene.loadAtlas(config.spriteKey, config.fileRoot); globalScene.loadAtlas(config.spriteKey, config.fileRoot);
} }
}); });
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => {
this.spriteConfigs.every((config) => { this.spriteConfigs.every((config) => {
if (config.isItem) { if (config.isItem) {
return true; return true;
@ -239,11 +239,11 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
// Ignore warnings for missing frames, because there will be a lot // Ignore warnings for missing frames, because there will be a lot
console.warn = () => { console.warn = () => {
}; };
const frameNames = this.scene.anims.generateFrameNames(config.spriteKey, { zeroPad: 4, suffix: ".png", start: 1, end: 128 }); const frameNames = globalScene.anims.generateFrameNames(config.spriteKey, { zeroPad: 4, suffix: ".png", start: 1, end: 128 });
console.warn = originalWarn; console.warn = originalWarn;
if (!(this.scene.anims.exists(config.spriteKey))) { if (!(globalScene.anims.exists(config.spriteKey))) {
this.scene.anims.create({ globalScene.anims.create({
key: config.spriteKey, key: config.spriteKey,
frames: frameNames, frames: frameNames,
frameRate: 10, frameRate: 10,
@ -257,8 +257,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
resolve(); resolve();
}); });
if (!this.scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
this.scene.load.start(); globalScene.load.start();
} }
}); });
} }
@ -337,8 +337,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
*/ */
playShinySparkles() { playShinySparkles() {
for (const sparkleConfig of this.shinySparkleSprites) { for (const sparkleConfig of this.shinySparkleSprites) {
this.scene.time.delayedCall(500, () => { globalScene.time.delayedCall(500, () => {
doShinySparkleAnim(this.scene, sparkleConfig.sprite, sparkleConfig.variant); doShinySparkleAnim(sparkleConfig.sprite, sparkleConfig.variant);
}); });
} }
} }
@ -429,7 +429,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
if (duration) { if (duration) {
sprite.setAlpha(0); sprite.setAlpha(0);
this.scene.tweens.add({ globalScene.tweens.add({
targets: sprite, targets: sprite,
alpha: alpha || 1, alpha: alpha || 1,
duration: duration, duration: duration,
@ -462,7 +462,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
*/ */
private untint(sprite, duration: integer, ease?: string): void { private untint(sprite, duration: integer, ease?: string): void {
if (duration) { if (duration) {
this.scene.tweens.add({ globalScene.tweens.add({
targets: sprite, targets: sprite,
alpha: 0, alpha: 0,
duration: duration, duration: duration,
@ -502,10 +502,3 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
return super.setVisible(value); return super.setVisible(value);
} }
} }
/**
* Interface is required so as not to override {@link Phaser.GameObjects.Container.scene}
*/
export default interface MysteryEncounterIntroVisuals {
scene: BattleScene
}

View File

@ -1,14 +1,14 @@
import BattleScene from "../battle-scene"; import { globalScene } from "#app/global-scene";
import Pokemon from "./pokemon"; import Pokemon from "./pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
export default class PokemonSpriteSparkleHandler { export default class PokemonSpriteSparkleHandler {
private sprites: Set<Phaser.GameObjects.Sprite>; private sprites: Set<Phaser.GameObjects.Sprite>;
setup(scene: BattleScene): void { setup(): void {
this.sprites = new Set(); this.sprites = new Set();
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.fixedInt(200), duration: Utils.fixedInt(200),
from: 0, from: 0,
to: 1, to: 1,
@ -37,7 +37,7 @@ export default class PokemonSpriteSparkleHandler {
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE");
if (pixel?.alpha) { if (pixel?.alpha) {
const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height ]; const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height ];
const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), "tera_sparkle"); const sparkle = globalScene.addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), "tera_sparkle");
sparkle.pipelineData["ignoreTimeTint"] = s.pipelineData["ignoreTimeTint"]; sparkle.pipelineData["ignoreTimeTint"] = s.pipelineData["ignoreTimeTint"];
sparkle.setName("sprite-tera-sparkle"); sparkle.setName("sprite-tera-sparkle");
sparkle.play("tera_sparkle"); sparkle.play("tera_sparkle");

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,21 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { import { getPokemonSpecies } from "#app/data/pokemon-species";
import type {
TrainerConfig, TrainerConfig,
TrainerPartyTemplate } from "#app/data/trainer-config";
import {
TrainerPartyCompoundTemplate, TrainerPartyCompoundTemplate,
TrainerPartyTemplate,
TrainerPoolTier, TrainerPoolTier,
TrainerSlot, TrainerSlot,
trainerConfigs, trainerConfigs,
trainerPartyTemplates, trainerPartyTemplates,
signatureSpecies signatureSpecies
} from "#app/data/trainer-config"; } from "#app/data/trainer-config";
import { EnemyPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { PersistentModifier } from "#app/modifier/modifier"; import type { PersistentModifier } from "#app/modifier/modifier";
import { trainerNamePools } from "#app/data/trainer-names"; import { trainerNamePools } from "#app/data/trainer-names";
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
import { getIsInitialized, initI18n } from "#app/plugins/i18n"; import { getIsInitialized, initI18n } from "#app/plugins/i18n";
@ -35,8 +37,8 @@ export default class Trainer extends Phaser.GameObjects.Container {
public name: string; public name: string;
public partnerName: string; public partnerName: string;
constructor(scene: BattleScene, trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: integer, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) { constructor(trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: integer, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) {
super(scene, -72, 80); super(globalScene, -72, 80);
this.config = trainerConfigs.hasOwnProperty(trainerType) this.config = trainerConfigs.hasOwnProperty(trainerType)
? trainerConfigs[trainerType] ? trainerConfigs[trainerType]
: trainerConfigs[TrainerType.ACE_TRAINER]; : trainerConfigs[TrainerType.ACE_TRAINER];
@ -80,9 +82,9 @@ export default class Trainer extends Phaser.GameObjects.Container {
console.log(Object.keys(trainerPartyTemplates)[Object.values(trainerPartyTemplates).indexOf(this.getPartyTemplate())]); console.log(Object.keys(trainerPartyTemplates)[Object.values(trainerPartyTemplates).indexOf(this.getPartyTemplate())]);
const getSprite = (hasShadow?: boolean, forceFemale?: boolean) => { const getSprite = (hasShadow?: boolean, forceFemale?: boolean) => {
const ret = this.scene.addFieldSprite(0, 0, this.config.getSpriteKey(variant === TrainerVariant.FEMALE || forceFemale, this.isDouble())); const ret = globalScene.addFieldSprite(0, 0, this.config.getSpriteKey(variant === TrainerVariant.FEMALE || forceFemale, this.isDouble()));
ret.setOrigin(0.5, 1); ret.setOrigin(0.5, 1);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow });
return ret; return ret;
}; };
@ -207,7 +209,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
getPartyTemplate(): TrainerPartyTemplate { getPartyTemplate(): TrainerPartyTemplate {
if (this.config.partyTemplateFunc) { if (this.config.partyTemplateFunc) {
return this.config.partyTemplateFunc(this.scene); return this.config.partyTemplateFunc();
} }
return this.config.partyTemplates[this.partyTemplateIndex]; return this.config.partyTemplates[this.partyTemplateIndex];
} }
@ -216,7 +218,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
const ret: number[] = []; const ret: number[] = [];
const partyTemplate = this.getPartyTemplate(); const partyTemplate = this.getPartyTemplate();
const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex); const difficultyWaveIndex = globalScene.gameMode.getWaveForDifficulty(waveIndex);
const baseLevel = 1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2); const baseLevel = 1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2);
if (this.isDouble() && partyTemplate.size < 2) { if (this.isDouble() && partyTemplate.size < 2) {
@ -261,12 +263,12 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
genPartyMember(index: integer): EnemyPokemon { genPartyMember(index: integer): EnemyPokemon {
const battle = this.scene.currentBattle; const battle = globalScene.currentBattle;
const level = battle.enemyLevels?.[index]!; // TODO: is this bang correct? const level = battle.enemyLevels?.[index]!; // TODO: is this bang correct?
let ret: EnemyPokemon; let ret: EnemyPokemon;
this.scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const template = this.getPartyTemplate(); const template = this.getPartyTemplate();
const strength: PartyMemberStrength = template.getStrength(index); const strength: PartyMemberStrength = template.getStrength(index);
@ -275,11 +277,11 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (!(this.config.trainerTypeDouble && this.isDouble() && !this.config.doubleOnly)) { if (!(this.config.trainerTypeDouble && this.isDouble() && !this.config.doubleOnly)) {
if (this.config.partyMemberFuncs.hasOwnProperty(index)) { if (this.config.partyMemberFuncs.hasOwnProperty(index)) {
ret = this.config.partyMemberFuncs[index](this.scene, level, strength); ret = this.config.partyMemberFuncs[index](level, strength);
return; return;
} }
if (this.config.partyMemberFuncs.hasOwnProperty(index - template.size)) { if (this.config.partyMemberFuncs.hasOwnProperty(index - template.size)) {
ret = this.config.partyMemberFuncs[index - template.size](this.scene, level, template.getStrength(index)); ret = this.config.partyMemberFuncs[index - template.size](level, template.getStrength(index));
return; return;
} }
} }
@ -364,23 +366,23 @@ export default class Trainer extends Phaser.GameObjects.Container {
let species = useNewSpeciesPool let species = useNewSpeciesPool
? getPokemonSpecies(newSpeciesPool[Math.floor(Utils.randSeedInt(newSpeciesPool.length))]) ? getPokemonSpecies(newSpeciesPool[Math.floor(Utils.randSeedInt(newSpeciesPool.length))])
: template.isSameSpecies(index) && index > offset : template.isSameSpecies(index) && index > offset
? getPokemonSpecies(battle.enemyParty[offset].species.getTrainerSpeciesForLevel(level, false, template.getStrength(offset), this.scene.currentBattle.waveIndex)) ? getPokemonSpecies(battle.enemyParty[offset].species.getTrainerSpeciesForLevel(level, false, template.getStrength(offset), globalScene.currentBattle.waveIndex))
: this.genNewPartyMemberSpecies(level, strength); : this.genNewPartyMemberSpecies(level, strength);
// If the species is from newSpeciesPool, we need to adjust it based on the level and strength // If the species is from newSpeciesPool, we need to adjust it based on the level and strength
if (newSpeciesPool) { if (newSpeciesPool) {
species = getPokemonSpecies(species.getSpeciesForLevel(level, true, true, strength, this.scene.currentBattle.waveIndex)); species = getPokemonSpecies(species.getSpeciesForLevel(level, true, true, strength, globalScene.currentBattle.waveIndex));
} }
ret = this.scene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); ret = globalScene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER);
}, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8)); }, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : globalScene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8));
return ret!; // TODO: is this bang correct? return ret!; // TODO: is this bang correct?
} }
genNewPartyMemberSpecies(level: integer, strength: PartyMemberStrength, attempt?: integer): PokemonSpecies { genNewPartyMemberSpecies(level: integer, strength: PartyMemberStrength, attempt?: integer): PokemonSpecies {
const battle = this.scene.currentBattle; const battle = globalScene.currentBattle;
const template = this.getPartyTemplate(); const template = this.getPartyTemplate();
let baseSpecies: PokemonSpecies; let baseSpecies: PokemonSpecies;
@ -395,10 +397,10 @@ export default class Trainer extends Phaser.GameObjects.Container {
const tierPool = this.config.speciesPools[tier]; const tierPool = this.config.speciesPools[tier];
baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool)); baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool));
} else { } else {
baseSpecies = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter); baseSpecies = globalScene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
} }
let ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); let ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex));
let retry = false; let retry = false;
console.log(ret.getName()); console.log(ret.getName());
@ -417,7 +419,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
console.log("Attempting reroll of species evolution to fit specialty type..."); console.log("Attempting reroll of species evolution to fit specialty type...");
let evoAttempt = 0; let evoAttempt = 0;
while (retry && evoAttempt++ < 10) { while (retry && evoAttempt++ < 10) {
ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex));
console.log(ret.name); console.log(ret.name);
if (this.config.specialtyTypes.find(t => ret.isOfType(t))) { if (this.config.specialtyTypes.find(t => ret.isOfType(t))) {
retry = false; retry = false;
@ -448,7 +450,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
checkDuplicateSpecies(species: PokemonSpecies, baseSpecies: PokemonSpecies): boolean { checkDuplicateSpecies(species: PokemonSpecies, baseSpecies: PokemonSpecies): boolean {
const staticPartyPokemon = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1); const staticPartyPokemon = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1);
const currentPartySpecies = this.scene.getEnemyParty().map(p => { const currentPartySpecies = globalScene.getEnemyParty().map(p => {
return p.species.speciesId; return p.species.speciesId;
}); });
return currentPartySpecies.includes(species.speciesId) || staticPartyPokemon.includes(baseSpecies.speciesId); return currentPartySpecies.includes(species.speciesId) || staticPartyPokemon.includes(baseSpecies.speciesId);
@ -459,10 +461,10 @@ export default class Trainer extends Phaser.GameObjects.Container {
trainerSlot = TrainerSlot.NONE; trainerSlot = TrainerSlot.NONE;
} }
const party = this.scene.getEnemyParty(); const party = globalScene.getEnemyParty();
const nonFaintedLegalPartyMembers = party.slice(this.scene.currentBattle.getBattlerCount()).filter(p => p.isAllowedInBattle()).filter(p => !trainerSlot || p.trainerSlot === trainerSlot); const nonFaintedLegalPartyMembers = party.slice(globalScene.currentBattle.getBattlerCount()).filter(p => p.isAllowedInBattle()).filter(p => !trainerSlot || p.trainerSlot === trainerSlot);
const partyMemberScores = nonFaintedLegalPartyMembers.map(p => { const partyMemberScores = nonFaintedLegalPartyMembers.map(p => {
const playerField = this.scene.getPlayerField().filter(p => p.isAllowedInBattle()); const playerField = globalScene.getPlayerField().filter(p => p.isAllowedInBattle());
let score = 0; let score = 0;
if (playerField.length > 0) { if (playerField.length > 0) {
@ -474,7 +476,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
score /= playerField.length; score /= playerField.length;
if (forSwitch && !p.isOnField()) { if (forSwitch && !p.isOnField()) {
this.scene.arena.findTagsOnSide(t => t instanceof ArenaTrapTag, ArenaTagSide.ENEMY).map(t => score *= (t as ArenaTrapTag).getMatchupScoreMultiplier(p)); globalScene.arena.findTagsOnSide(t => t instanceof ArenaTrapTag, ArenaTagSide.ENEMY).map(t => score *= (t as ArenaTrapTag).getMatchupScoreMultiplier(p));
} }
} }
@ -506,7 +508,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (maxScorePartyMemberIndexes.length > 1) { if (maxScorePartyMemberIndexes.length > 1) {
let rand: integer; let rand: integer;
this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2); globalScene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), globalScene.currentBattle.turn << 2);
return maxScorePartyMemberIndexes[rand!]; return maxScorePartyMemberIndexes[rand!];
} }
@ -539,7 +541,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
loadAssets(): Promise<void> { loadAssets(): Promise<void> {
return this.config.loadAssets(this.scene, this.variant); return this.config.loadAssets(this.variant);
} }
initSprite(): void { initSprite(): void {
@ -627,7 +629,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
if (duration) { if (duration) {
tintSprite.setAlpha(0); tintSprite.setAlpha(0);
this.scene.tweens.add({ globalScene.tweens.add({
targets: tintSprite, targets: tintSprite,
alpha: alpha || 1, alpha: alpha || 1,
duration: duration, duration: duration,
@ -643,7 +645,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
const tintSprites = this.getTintSprites(); const tintSprites = this.getTintSprites();
tintSprites.map(tintSprite => { tintSprites.map(tintSprite => {
if (duration) { if (duration) {
this.scene.tweens.add({ globalScene.tweens.add({
targets: tintSprite, targets: tintSprite,
alpha: 0, alpha: 0,
duration: duration, duration: duration,
@ -660,7 +662,3 @@ export default class Trainer extends Phaser.GameObjects.Container {
}); });
} }
} }
export default interface Trainer {
scene: BattleScene
}

View File

@ -1,14 +1,17 @@
import i18next from "i18next"; import i18next from "i18next";
import { classicFixedBattles, FixedBattleConfig, FixedBattleConfigs } from "./battle"; import type { FixedBattleConfigs } from "./battle";
import BattleScene from "./battle-scene"; import { classicFixedBattles, FixedBattleConfig } from "./battle";
import { allChallenges, applyChallenges, Challenge, ChallengeType, copyChallenge } from "./data/challenge"; import type { Challenge } from "./data/challenge";
import PokemonSpecies, { allSpecies } from "./data/pokemon-species"; import { allChallenges, applyChallenges, ChallengeType, copyChallenge } from "./data/challenge";
import { Arena } from "./field/arena"; import type PokemonSpecies from "./data/pokemon-species";
import { allSpecies } from "./data/pokemon-species";
import type { Arena } from "./field/arena";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import * as Utils from "./utils"; import * as Utils from "./utils";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Challenges } from "./enums/challenges"; import { Challenges } from "./enums/challenges";
import { globalScene } from "#app/global-scene";
export enum GameModes { export enum GameModes {
CLASSIC, CLASSIC,
@ -109,16 +112,15 @@ export class GameMode implements GameModeConfig {
} }
/** /**
* @param scene current BattleScene
* @returns either: * @returns either:
* - random biome for Daily mode * - random biome for Daily mode
* - override from overrides.ts * - override from overrides.ts
* - Town * - Town
*/ */
getStartingBiome(scene: BattleScene): Biome { getStartingBiome(): Biome {
switch (this.modeId) { switch (this.modeId) {
case GameModes.DAILY: case GameModes.DAILY:
return scene.generateRandomBiome(this.getWaveForDifficulty(1)); return globalScene.generateRandomBiome(this.getWaveForDifficulty(1));
default: default:
return Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN; return Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN;
} }
@ -136,8 +138,8 @@ export class GameMode implements GameModeConfig {
/** /**
* Determines whether or not to generate a trainer * Determines whether or not to generate a trainer
* @param waveIndex the current floor the player is on (trainer sprites fail to generate on X1 floors) * @param waveIndex the current floor the player is on (trainer sprites fail to generate on X1 floors)
* @param arena the arena that contains the scene and functions * @param arena the current {@linkcode Arena}
* @returns true if a trainer should be generated, false otherwise * @returns `true` if a trainer should be generated, `false` otherwise
*/ */
isWaveTrainer(waveIndex: integer, arena: Arena): boolean { isWaveTrainer(waveIndex: integer, arena: Arena): boolean {
/** /**
@ -146,14 +148,13 @@ export class GameMode implements GameModeConfig {
if (this.isDaily) { if (this.isDaily) {
return waveIndex % 10 === 5 || (!(waveIndex % 10) && waveIndex > 10 && !this.isWaveFinal(waveIndex)); return waveIndex % 10 === 5 || (!(waveIndex % 10) && waveIndex > 10 && !this.isWaveFinal(waveIndex));
} }
if ((waveIndex % 30) === (arena.scene.offsetGym ? 0 : 20) && !this.isWaveFinal(waveIndex)) { if ((waveIndex % 30) === (globalScene.offsetGym ? 0 : 20) && !this.isWaveFinal(waveIndex)) {
return true; return true;
} else if (waveIndex % 10 !== 1 && waveIndex % 10) { } else if (waveIndex % 10 !== 1 && waveIndex % 10) {
/** /**
* Do not check X1 floors since there's a bug that stops trainer sprites from appearing * Do not check X1 floors since there's a bug that stops trainer sprites from appearing
* after a X0 full party heal * after a X0 full party heal
*/ */
const trainerChance = arena.getTrainerChance(); const trainerChance = arena.getTrainerChance();
let allowTrainerBattle = true; let allowTrainerBattle = true;
if (trainerChance) { if (trainerChance) {
@ -163,11 +164,11 @@ export class GameMode implements GameModeConfig {
if (w === waveIndex) { if (w === waveIndex) {
continue; continue;
} }
if ((w % 30) === (arena.scene.offsetGym ? 0 : 20) || this.isFixedBattle(w)) { if ((w % 30) === (globalScene.offsetGym ? 0 : 20) || this.isFixedBattle(w)) {
allowTrainerBattle = false; allowTrainerBattle = false;
break; break;
} else if (w < waveIndex) { } else if (w < waveIndex) {
arena.scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const waveTrainerChance = arena.getTrainerChance(); const waveTrainerChance = arena.getTrainerChance();
if (!Utils.randSeedInt(waveTrainerChance)) { if (!Utils.randSeedInt(waveTrainerChance)) {
allowTrainerBattle = false; allowTrainerBattle = false;

7
src/global-scene.ts Normal file
View File

@ -0,0 +1,7 @@
import type BattleScene from "#app/battle-scene";
export let globalScene: BattleScene;
export function initGlobalScene(scene: BattleScene): void {
globalScene = scene;
}

View File

@ -7,17 +7,17 @@ import pad_xbox360 from "./configs/inputs/pad_xbox360";
import pad_dualshock from "./configs/inputs/pad_dualshock"; import pad_dualshock from "./configs/inputs/pad_dualshock";
import pad_procon from "./configs/inputs/pad_procon"; import pad_procon from "./configs/inputs/pad_procon";
import { Mode } from "./ui/ui"; import { Mode } from "./ui/ui";
import SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler"; import type SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler";
import SettingsKeyboardUiHandler from "./ui/settings/settings-keyboard-ui-handler"; import type SettingsKeyboardUiHandler from "./ui/settings/settings-keyboard-ui-handler";
import cfg_keyboard_qwerty from "./configs/inputs/cfg_keyboard_qwerty"; import cfg_keyboard_qwerty from "./configs/inputs/cfg_keyboard_qwerty";
import { import {
assign, assign,
getButtonWithKeycode, getButtonWithKeycode,
getIconForLatestInput, swap, getIconForLatestInput, swap,
} from "#app/configs/inputs/configHandler"; } from "#app/configs/inputs/configHandler";
import BattleScene from "./battle-scene"; import { globalScene } from "#app/global-scene";
import { SettingGamepad } from "#app/system/settings/settings-gamepad"; import type { SettingGamepad } from "#app/system/settings/settings-gamepad";
import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import type { SettingKeyboard } from "#app/system/settings/settings-keyboard";
import TouchControl from "#app/touch-controls"; import TouchControl from "#app/touch-controls";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { Device } from "#enums/devices"; import { Device } from "#enums/devices";
@ -75,7 +75,6 @@ const repeatInputDelayMillis = 250;
*/ */
export class InputsController { export class InputsController {
private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = new Array(); private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = new Array();
private scene: BattleScene;
public events: Phaser.Events.EventEmitter; public events: Phaser.Events.EventEmitter;
private buttonLock: Button[] = new Array(); private buttonLock: Button[] = new Array();
@ -96,17 +95,13 @@ export class InputsController {
/** /**
* Initializes a new instance of the game control system, setting up initial state and configurations. * Initializes a new instance of the game control system, setting up initial state and configurations.
* *
* @param scene - The Phaser scene associated with this instance.
*
* @remarks * @remarks
* This constructor initializes the game control system with necessary setups for handling inputs. * This constructor initializes the game control system with necessary setups for handling inputs.
* It prepares an interactions array indexed by button identifiers and configures default states for each button. * It prepares an interactions array indexed by button identifiers and configures default states for each button.
* Specific buttons like MENU and STATS are set not to repeat their actions. * Specific buttons like MENU and STATS are set not to repeat their actions.
* It concludes by calling the `init` method to complete the setup. * It concludes by calling the `init` method to complete the setup.
*/ */
constructor() {
constructor(scene: BattleScene) {
this.scene = scene;
this.selectedDevice = { this.selectedDevice = {
[Device.GAMEPAD]: null, [Device.GAMEPAD]: null,
[Device.KEYBOARD]: "default" [Device.KEYBOARD]: "default"
@ -134,14 +129,14 @@ export class InputsController {
* Additionally, it manages the game's behavior when it loses focus to prevent unwanted game actions during this state. * Additionally, it manages the game's behavior when it loses focus to prevent unwanted game actions during this state.
*/ */
init(): void { init(): void {
this.events = this.scene.game.events; this.events = globalScene.game.events;
this.scene.game.events.on(Phaser.Core.Events.BLUR, () => { globalScene.game.events.on(Phaser.Core.Events.BLUR, () => {
this.loseFocus(); this.loseFocus();
}); });
if (typeof this.scene.input.gamepad !== "undefined") { if (typeof globalScene.input.gamepad !== "undefined") {
this.scene.input.gamepad?.on("connected", function (thisGamepad) { globalScene.input.gamepad?.on("connected", function (thisGamepad) {
if (!thisGamepad) { if (!thisGamepad) {
return; return;
} }
@ -150,25 +145,25 @@ export class InputsController {
this.onReconnect(thisGamepad); this.onReconnect(thisGamepad);
}, this); }, this);
this.scene.input.gamepad?.on("disconnected", function (thisGamepad) { globalScene.input.gamepad?.on("disconnected", function (thisGamepad) {
this.onDisconnect(thisGamepad); // when a gamepad is disconnected this.onDisconnect(thisGamepad); // when a gamepad is disconnected
}, this); }, this);
// Check to see if the gamepad has already been setup by the browser // Check to see if the gamepad has already been setup by the browser
this.scene.input.gamepad?.refreshPads(); globalScene.input.gamepad?.refreshPads();
if (this.scene.input.gamepad?.total) { if (globalScene.input.gamepad?.total) {
this.refreshGamepads(); this.refreshGamepads();
for (const thisGamepad of this.gamepads) { for (const thisGamepad of this.gamepads) {
this.scene.input.gamepad.emit("connected", thisGamepad); globalScene.input.gamepad.emit("connected", thisGamepad);
} }
} }
this.scene.input.gamepad?.on("down", this.gamepadButtonDown, this); globalScene.input.gamepad?.on("down", this.gamepadButtonDown, this);
this.scene.input.gamepad?.on("up", this.gamepadButtonUp, this); globalScene.input.gamepad?.on("up", this.gamepadButtonUp, this);
this.scene.input.keyboard?.on("keydown", this.keyboardKeyDown, this); globalScene.input.keyboard?.on("keydown", this.keyboardKeyDown, this);
this.scene.input.keyboard?.on("keyup", this.keyboardKeyUp, this); globalScene.input.keyboard?.on("keyup", this.keyboardKeyUp, this);
} }
this.touchControls = new TouchControl(this.scene); this.touchControls = new TouchControl();
this.moveTouchControlsHandler = new MoveTouchControlsHandler(this.touchControls); this.moveTouchControlsHandler = new MoveTouchControlsHandler(this.touchControls);
} }
@ -238,7 +233,7 @@ export class InputsController {
if (gamepadName) { if (gamepadName) {
this.selectedDevice[Device.GAMEPAD] = gamepadName.toLowerCase(); this.selectedDevice[Device.GAMEPAD] = gamepadName.toLowerCase();
} }
const handler = this.scene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; const handler = globalScene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler;
handler && handler.updateChosenGamepadDisplay(); handler && handler.updateChosenGamepadDisplay();
} }
@ -251,7 +246,7 @@ export class InputsController {
if (layoutKeyboard) { if (layoutKeyboard) {
this.selectedDevice[Device.KEYBOARD] = layoutKeyboard.toLowerCase(); this.selectedDevice[Device.KEYBOARD] = layoutKeyboard.toLowerCase();
} }
const handler = this.scene.ui?.handlers[Mode.SETTINGS_KEYBOARD] as SettingsKeyboardUiHandler; const handler = globalScene.ui?.handlers[Mode.SETTINGS_KEYBOARD] as SettingsKeyboardUiHandler;
handler && handler.updateChosenKeyboardDisplay(); handler && handler.updateChosenKeyboardDisplay();
} }
@ -296,10 +291,10 @@ export class InputsController {
const config = deepCopy(this.getConfig(gamepadID)) as InterfaceConfig; const config = deepCopy(this.getConfig(gamepadID)) as InterfaceConfig;
config.custom = this.configs[gamepadID]?.custom || { ...config.default }; config.custom = this.configs[gamepadID]?.custom || { ...config.default };
this.configs[gamepadID] = config; this.configs[gamepadID] = config;
this.scene.gameData?.saveMappingConfigs(gamepadID, this.configs[gamepadID]); globalScene.gameData?.saveMappingConfigs(gamepadID, this.configs[gamepadID]);
} }
this.lastSource = "gamepad"; this.lastSource = "gamepad";
const handler = this.scene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; const handler = globalScene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler;
handler && handler.updateChosenGamepadDisplay(); handler && handler.updateChosenGamepadDisplay();
} }
@ -311,7 +306,7 @@ export class InputsController {
const config = deepCopy(this.getConfigKeyboard(layout)) as InterfaceConfig; const config = deepCopy(this.getConfigKeyboard(layout)) as InterfaceConfig;
config.custom = this.configs[layout]?.custom || { ...config.default }; config.custom = this.configs[layout]?.custom || { ...config.default };
this.configs[layout] = config; this.configs[layout] = config;
this.scene.gameData?.saveMappingConfigs(this.selectedDevice[Device.KEYBOARD], this.configs[layout]); globalScene.gameData?.saveMappingConfigs(this.selectedDevice[Device.KEYBOARD], this.configs[layout]);
} }
this.initChosenLayoutKeyboard(this.selectedDevice[Device.KEYBOARD]); this.initChosenLayoutKeyboard(this.selectedDevice[Device.KEYBOARD]);
} }
@ -326,7 +321,7 @@ export class InputsController {
*/ */
refreshGamepads(): void { refreshGamepads(): void {
// Sometimes, gamepads are undefined. For some reason. // Sometimes, gamepads are undefined. For some reason.
this.gamepads = this.scene.input.gamepad?.gamepads.filter(function (el) { this.gamepads = globalScene.input.gamepad?.gamepads.filter(function (el) {
return el !== null; return el !== null;
}) ?? []; }) ?? [];
@ -409,7 +404,7 @@ export class InputsController {
return; return;
} }
this.lastSource = "gamepad"; this.lastSource = "gamepad";
if (!this.selectedDevice[Device.GAMEPAD] || (this.scene.ui.getMode() !== Mode.GAMEPAD_BINDING && this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase())) { if (!this.selectedDevice[Device.GAMEPAD] || (globalScene.ui.getMode() !== Mode.GAMEPAD_BINDING && this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase())) {
this.setChosenGamepad(pad.id); this.setChosenGamepad(pad.id);
} }
if (!this.gamepadSupport || pad.id.toLowerCase() !== this.selectedDevice[Device.GAMEPAD].toLowerCase()) { if (!this.gamepadSupport || pad.id.toLowerCase() !== this.selectedDevice[Device.GAMEPAD].toLowerCase()) {

View File

@ -1,5 +1,5 @@
import { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
export default interface HeldModifierConfig { export default interface HeldModifierConfig {
modifier: PokemonHeldItemModifierType | PokemonHeldItemModifier; modifier: PokemonHeldItemModifierType | PokemonHeldItemModifier;

View File

@ -1,5 +1,6 @@
import { globalScene } from "#app/global-scene";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import Pokemon from "./field/pokemon"; import type Pokemon from "./field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
/** /**
@ -12,7 +13,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string {
return "Missigno"; return "Missigno";
} }
switch (pokemon.scene.currentBattle.battleSpec) { switch (globalScene.currentBattle.battleSpec) {
case BattleSpec.DEFAULT: case BattleSpec.DEFAULT:
return !pokemon.isPlayer() return !pokemon.isPlayer()
? pokemon.hasTrainer() ? pokemon.hasTrainer()

View File

@ -1,4 +1,4 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms"; import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms";
import { getBerryEffectDescription, getBerryName } from "#app/data/berry"; import { getBerryEffectDescription, getBerryName } from "#app/data/berry";
@ -8,14 +8,16 @@ import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } f
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeCondition, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeCondition, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
import { getStatusEffectDescriptor } from "#app/data/status-effect"; import { getStatusEffectDescriptor } from "#app/data/status-effect";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { AddPokeballModifier, AddVoucherModifier, AttackTypeBoosterModifier, BaseStatModifier, BerryModifier, BoostBugSpawnModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, EvolutionStatBoosterModifier, EvoTrackerModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, HealingBoosterModifier, HealShopCostModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, IvScannerModifier, LevelIncrementBoosterModifier, LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, PokemonInstantReviveModifier, PokemonLevelIncrementModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PokemonNatureChangeModifier, PokemonNatureWeightModifier, PokemonPpRestoreModifier, PokemonPpUpModifier, PokemonStatusHealModifier, PreserveBerryModifier, RememberMoveModifier, ResetNegativeStatStageModifier, ShinyRateBoosterModifier, SpeciesCritBoosterModifier, SpeciesStatBoosterModifier, SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerastallizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, type EnemyPersistentModifier, type Modifier, type PersistentModifier, TempExtraModifierModifier, CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier"; import { AddPokeballModifier, AddVoucherModifier, AttackTypeBoosterModifier, BaseStatModifier, BerryModifier, BoostBugSpawnModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, CritBoosterModifier, DamageMoneyRewardModifier, DoubleBattleChanceBoosterModifier, EnemyAttackStatusEffectChanceModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, EvolutionItemModifier, EvolutionStatBoosterModifier, EvoTrackerModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, GigantamaxAccessModifier, HealingBoosterModifier, HealShopCostModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, IvScannerModifier, LevelIncrementBoosterModifier, LockModifierTiersModifier, MapModifier, MegaEvolutionAccessModifier, MoneyInterestModifier, MoneyMultiplierModifier, MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, PokemonInstantReviveModifier, PokemonLevelIncrementModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PokemonNatureChangeModifier, PokemonNatureWeightModifier, PokemonPpRestoreModifier, PokemonPpUpModifier, PokemonStatusHealModifier, PreserveBerryModifier, RememberMoveModifier, ResetNegativeStatStageModifier, ShinyRateBoosterModifier, SpeciesCritBoosterModifier, SpeciesStatBoosterModifier, SurviveDamageModifier, SwitchEffectTransferModifier, TempCritBoosterModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, TerastallizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier, type EnemyPersistentModifier, type Modifier, type PersistentModifier, TempExtraModifierModifier, CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { Unlockables } from "#app/system/unlockables"; import { Unlockables } from "#app/system/unlockables";
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher"; import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system/voucher";
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler";
import PartyUiHandler from "#app/ui/party-ui-handler";
import { getModifierTierTextTint } from "#app/ui/text"; import { getModifierTierTextTint } from "#app/ui/text";
import { formatMoney, getEnumKeys, getEnumValues, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils"; import { formatMoney, getEnumKeys, getEnumValues, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
@ -26,7 +28,8 @@ import { Nature } from "#enums/nature";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { getStatKey, PermanentStat, Stat, TEMP_BATTLE_STATS, TempBattleStat } from "#enums/stat"; import type { PermanentStat, TempBattleStat } from "#enums/stat";
import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import i18next from "i18next"; import i18next from "i18next";
@ -64,7 +67,7 @@ export class ModifierType {
return i18next.t(`${this.localeKey}.name` as any); return i18next.t(`${this.localeKey}.name` as any);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t(`${this.localeKey}.description` as any); return i18next.t(`${this.localeKey}.description` as any);
} }
@ -206,12 +209,12 @@ class AddPokeballModifierType extends ModifierType {
}); });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", { return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", {
"modifierCount": this.count, "modifierCount": this.count,
"pokeballName": getPokeballName(this.pokeballType), "pokeballName": getPokeballName(this.pokeballType),
"catchRate": getPokeballCatchMultiplier(this.pokeballType) > -1 ? `${getPokeballCatchMultiplier(this.pokeballType)}x` : "100%", "catchRate": getPokeballCatchMultiplier(this.pokeballType) > -1 ? `${getPokeballCatchMultiplier(this.pokeballType)}x` : "100%",
"pokeballAmount": `${scene.pokeballCounts[this.pokeballType]}`, "pokeballAmount": `${globalScene.pokeballCounts[this.pokeballType]}`,
}); });
} }
} }
@ -233,7 +236,7 @@ class AddVoucherModifierType extends ModifierType {
}); });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.AddVoucherModifierType.description", { return i18next.t("modifierType:ModifierType.AddVoucherModifierType.description", {
"modifierCount": this.count, "modifierCount": this.count,
"voucherTypeName": getVoucherTypeName(this.voucherType), "voucherTypeName": getVoucherTypeName(this.voucherType),
@ -255,8 +258,8 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) { constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) {
super(localeKey, iconImage, newModifierFunc, (pokemon: PlayerPokemon) => { super(localeKey, iconImage, newModifierFunc, (pokemon: PlayerPokemon) => {
const dummyModifier = this.newModifier(pokemon); const dummyModifier = this.newModifier(pokemon);
const matchingModifier = pokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier)) as PokemonHeldItemModifier; const matchingModifier = globalScene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(dummyModifier)) as PokemonHeldItemModifier;
const maxStackCount = dummyModifier.getMaxStackCount(pokemon.scene); const maxStackCount = dummyModifier.getMaxStackCount();
if (!maxStackCount) { if (!maxStackCount) {
return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { "pokemonName": getPokemonNameWithAffix(pokemon) }); return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { "pokemonName": getPokemonNameWithAffix(pokemon) });
} }
@ -291,7 +294,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
this.healStatus = healStatus; this.healStatus = healStatus;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return this.restorePoints return this.restorePoints
? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", { ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", {
restorePoints: this.restorePoints, restorePoints: this.restorePoints,
@ -321,7 +324,7 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
}; };
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { restorePercent: this.restorePercent }); return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { restorePercent: this.restorePercent });
} }
} }
@ -337,7 +340,7 @@ export class PokemonStatusHealModifierType extends PokemonModifierType {
})); }));
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description");
} }
} }
@ -369,7 +372,7 @@ export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
this.restorePoints = restorePoints; this.restorePoints = restorePoints;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return this.restorePoints > -1 return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { restorePoints: this.restorePoints }) ? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { restorePoints: this.restorePoints })
: i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.extra.fully") : i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.extra.fully")
@ -392,7 +395,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
this.restorePoints = restorePoints; this.restorePoints = restorePoints;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return this.restorePoints > -1 return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { restorePoints: this.restorePoints }) ? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { restorePoints: this.restorePoints })
: i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.extra.fully") : i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.extra.fully")
@ -417,7 +420,7 @@ export class PokemonPpUpModifierType extends PokemonMoveModifierType {
this.upPoints = upPoints; this.upPoints = upPoints;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints }); return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints });
} }
} }
@ -441,7 +444,7 @@ export class PokemonNatureChangeModifierType extends PokemonModifierType {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { natureName: getNatureName(this.nature) }); return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { natureName: getNatureName(this.nature) });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { natureName: getNatureName(this.nature, true, true, true) }); return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { natureName: getNatureName(this.nature, true, true, true) });
} }
} }
@ -467,7 +470,7 @@ export class DoubleBattleChanceBoosterModifierType extends ModifierType {
this.maxBattles = maxBattles; this.maxBattles = maxBattles;
} }
getDescription(_scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", { return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", {
battleCount: this.maxBattles battleCount: this.maxBattles
}); });
@ -492,7 +495,7 @@ export class TempStatStageBoosterModifierType extends ModifierType implements Ge
return i18next.t(`modifierType:TempStatStageBoosterItem.${this.nameKey}`); return i18next.t(`modifierType:TempStatStageBoosterItem.${this.nameKey}`);
} }
getDescription(_scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t(getStatKey(this.stat)), stat: i18next.t(getStatKey(this.stat)),
amount: i18next.t(`modifierType:ModifierType.TempStatStageBoosterModifierType.extra.${this.quantityKey}`) amount: i18next.t(`modifierType:ModifierType.TempStatStageBoosterModifierType.extra.${this.quantityKey}`)
@ -517,7 +520,7 @@ export class BerryModifierType extends PokemonHeldItemModifierType implements Ge
return getBerryName(this.berryType); return getBerryName(this.berryType);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return getBerryEffectDescription(this.berryType); return getBerryEffectDescription(this.berryType);
} }
@ -563,7 +566,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
return i18next.t(`modifierType:AttackTypeBoosterItem.${AttackTypeBoosterItem[this.moveType]?.toLowerCase()}`); return i18next.t(`modifierType:AttackTypeBoosterItem.${AttackTypeBoosterItem[this.moveType]?.toLowerCase()}`);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
// TODO: Need getTypeName? // TODO: Need getTypeName?
return i18next.t("modifierType:ModifierType.AttackTypeBoosterModifierType.description", { moveType: i18next.t(`pokemonInfo:Type.${Type[this.moveType]}`) }); return i18next.t("modifierType:ModifierType.AttackTypeBoosterModifierType.description", { moveType: i18next.t(`pokemonInfo:Type.${Type[this.moveType]}`) });
} }
@ -600,9 +603,9 @@ export class PokemonLevelIncrementModifierType extends PokemonModifierType {
super(localeKey, iconImage, (_type, args) => new PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null); super(localeKey, iconImage, (_type, args) => new PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
let levels = 1; let levels = 1;
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier); const hasCandyJar = globalScene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier);
if (hasCandyJar) { if (hasCandyJar) {
levels += hasCandyJar.stackCount; levels += hasCandyJar.stackCount;
} }
@ -615,9 +618,9 @@ export class AllPokemonLevelIncrementModifierType extends ModifierType {
super(localeKey, iconImage, (_type, _args) => new PokemonLevelIncrementModifier(this, -1)); super(localeKey, iconImage, (_type, _args) => new PokemonLevelIncrementModifier(this, -1));
} }
getDescription(scene: BattleScene): string { getDescription(): string {
let levels = 1; let levels = 1;
const hasCandyJar = scene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier); const hasCandyJar = globalScene.modifiers.find(modifier => modifier instanceof LevelIncrementBoosterModifier);
if (hasCandyJar) { if (hasCandyJar) {
levels += hasCandyJar.stackCount; levels += hasCandyJar.stackCount;
} }
@ -641,7 +644,7 @@ export class BaseStatBoosterModifierType extends PokemonHeldItemModifierType imp
return i18next.t(`modifierType:BaseStatBoosterItem.${this.key}`); return i18next.t(`modifierType:BaseStatBoosterItem.${this.key}`);
} }
getDescription(_scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.BaseStatBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) }); return i18next.t("modifierType:ModifierType.BaseStatBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) });
} }
@ -661,7 +664,7 @@ export class PokemonBaseStatTotalModifierType extends PokemonHeldItemModifierTyp
this.statModifier = statModifier; this.statModifier = statModifier;
} }
override getDescription(scene: BattleScene): string { override getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonBaseStatTotalModifierType.description", { return i18next.t("modifierType:ModifierType.PokemonBaseStatTotalModifierType.description", {
increaseDecrease: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.increase" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.decrease"), increaseDecrease: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.increase" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.decrease"),
blessCurse: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.blessed" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.cursed"), blessCurse: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.blessed" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.cursed"),
@ -687,7 +690,7 @@ export class PokemonBaseStatFlatModifierType extends PokemonHeldItemModifierType
this.stats = stats; this.stats = stats;
} }
override getDescription(scene: BattleScene): string { override getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonBaseStatFlatModifierType.description", { return i18next.t("modifierType:ModifierType.PokemonBaseStatFlatModifierType.description", {
stats: this.stats.map(stat => i18next.t(getStatKey(stat))).join("/"), stats: this.stats.map(stat => i18next.t(getStatKey(stat))).join("/"),
statValue: this.statModifier, statValue: this.statModifier,
@ -708,7 +711,7 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType {
this.descriptionKey = descriptionKey!; // TODO: is this bang correct? this.descriptionKey = descriptionKey!; // TODO: is this bang correct?
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t(`${this.descriptionKey || "modifierType:ModifierType.AllPokemonFullHpRestoreModifierType"}.description` as any); return i18next.t(`${this.descriptionKey || "modifierType:ModifierType.AllPokemonFullHpRestoreModifierType"}.description` as any);
} }
} }
@ -730,10 +733,10 @@ export class MoneyRewardModifierType extends ModifierType {
this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey; this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
const moneyAmount = new NumberHolder(scene.getWaveMoneyAmount(this.moneyMultiplier)); const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
const formattedMoney = formatMoney(scene.moneyFormat, moneyAmount.value); const formattedMoney = formatMoney(globalScene.moneyFormat, moneyAmount.value);
return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", { return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", {
moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any), moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any),
@ -751,7 +754,7 @@ export class ExpBoosterModifierType extends ModifierType {
this.boostPercent = boostPercent; this.boostPercent = boostPercent;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.ExpBoosterModifierType.description", { boostPercent: this.boostPercent }); return i18next.t("modifierType:ModifierType.ExpBoosterModifierType.description", { boostPercent: this.boostPercent });
} }
} }
@ -765,7 +768,7 @@ export class PokemonExpBoosterModifierType extends PokemonHeldItemModifierType {
this.boostPercent = boostPercent; this.boostPercent = boostPercent;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonExpBoosterModifierType.description", { boostPercent: this.boostPercent }); return i18next.t("modifierType:ModifierType.PokemonExpBoosterModifierType.description", { boostPercent: this.boostPercent });
} }
} }
@ -775,7 +778,7 @@ export class PokemonFriendshipBoosterModifierType extends PokemonHeldItemModifie
super(localeKey, iconImage, (_type, args) => new PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id)); super(localeKey, iconImage, (_type, args) => new PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id));
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonFriendshipBoosterModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonFriendshipBoosterModifierType.description");
} }
} }
@ -789,7 +792,7 @@ export class PokemonMoveAccuracyBoosterModifierType extends PokemonHeldItemModif
this.amount = amount; this.amount = amount;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonMoveAccuracyBoosterModifierType.description", { accuracyAmount: this.amount }); return i18next.t("modifierType:ModifierType.PokemonMoveAccuracyBoosterModifierType.description", { accuracyAmount: this.amount });
} }
} }
@ -799,7 +802,7 @@ export class PokemonMultiHitModifierType extends PokemonHeldItemModifierType {
super(localeKey, iconImage, (type, args) => new PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id)); super(localeKey, iconImage, (type, args) => new PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id));
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.PokemonMultiHitModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonMultiHitModifierType.description");
} }
} }
@ -826,8 +829,8 @@ export class TmModifierType extends PokemonModifierType {
}); });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t(scene.enableMoveInfo ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" : "modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name }); return i18next.t(globalScene.enableMoveInfo ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" : "modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name });
} }
} }
@ -855,7 +858,7 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`); return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description"); return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description");
} }
@ -894,7 +897,7 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G
return i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.formChangeItem]}`); return i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.formChangeItem]}`);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description"); return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description");
} }
@ -914,7 +917,7 @@ export class FusePokemonModifierType extends PokemonModifierType {
}); });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description"); return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description");
} }
} }
@ -1149,12 +1152,12 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
const formChangeItemPool = [ ...new Set(party.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)).map(p => { const formChangeItemPool = [ ...new Set(party.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)).map(p => {
const formChanges = pokemonFormChanges[p.species.speciesId]; const formChanges = pokemonFormChanges[p.species.speciesId];
let formChangeItemTriggers = formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || party[0].scene.getModifiers(MegaEvolutionAccessModifier).length) let formChangeItemTriggers = formChanges.filter(fc => ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || globalScene.getModifiers(MegaEvolutionAccessModifier).length)
&& ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || party[0].scene.getModifiers(GigantamaxAccessModifier).length) && ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || globalScene.getModifiers(GigantamaxAccessModifier).length)
&& (!fc.conditions.length || fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)).length) && (!fc.conditions.length || fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)).length)
&& (fc.preFormKey === p.getFormKey())) && (fc.preFormKey === p.getFormKey()))
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) .map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
.filter(t => t && t.active && !p.scene.findModifier(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === p.id && m.formChangeItem === t.item)); .filter(t => t && t.active && !globalScene.findModifier(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === p.id && m.formChangeItem === t.item));
if (p.species.speciesId === Species.NECROZMA) { if (p.species.speciesId === Species.NECROZMA) {
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break... // technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
@ -1162,7 +1165,7 @@ class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator {
let foundULTRA_Z = false, let foundULTRA_Z = false,
foundN_LUNA = false, foundN_LUNA = false,
foundN_SOLAR = false; foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, i) => { formChangeItemTriggers.forEach((fc, _i) => {
switch (fc.item) { switch (fc.item) {
case FormChangeItem.ULTRANECROZIUM_Z: case FormChangeItem.ULTRANECROZIUM_Z:
foundULTRA_Z = true; foundULTRA_Z = true;
@ -1207,7 +1210,7 @@ export class TerastallizeModifierType extends PokemonHeldItemModifierType implem
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) }); return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) }); return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
} }
@ -1225,7 +1228,7 @@ export class ContactHeldItemTransferChanceModifierType extends PokemonHeldItemMo
this.chancePercent = chancePercent; this.chancePercent = chancePercent;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.ContactHeldItemTransferChanceModifierType.description", { chancePercent: this.chancePercent }); return i18next.t("modifierType:ModifierType.ContactHeldItemTransferChanceModifierType.description", { chancePercent: this.chancePercent });
} }
} }
@ -1235,7 +1238,7 @@ export class TurnHeldItemTransferModifierType extends PokemonHeldItemModifierTyp
super(localeKey, iconImage, (type, args) => new TurnHeldItemTransferModifier(type, (args[0] as Pokemon).id), group, soundName); super(localeKey, iconImage, (type, args) => new TurnHeldItemTransferModifier(type, (args[0] as Pokemon).id), group, soundName);
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.TurnHeldItemTransferModifierType.description"); return i18next.t("modifierType:ModifierType.TurnHeldItemTransferModifierType.description");
} }
} }
@ -1245,13 +1248,13 @@ export class EnemyAttackStatusEffectChanceModifierType extends ModifierType {
private effect: StatusEffect; private effect: StatusEffect;
constructor(localeKey: string, iconImage: string, chancePercent: integer, effect: StatusEffect, stackCount?: integer) { constructor(localeKey: string, iconImage: string, chancePercent: integer, effect: StatusEffect, stackCount?: integer) {
super(localeKey, iconImage, (type, args) => new EnemyAttackStatusEffectChanceModifier(type, effect, chancePercent, stackCount), "enemy_status_chance"); super(localeKey, iconImage, (type, _args) => new EnemyAttackStatusEffectChanceModifier(type, effect, chancePercent, stackCount), "enemy_status_chance");
this.chancePercent = chancePercent; this.chancePercent = chancePercent;
this.effect = effect; this.effect = effect;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.EnemyAttackStatusEffectChanceModifierType.description", { return i18next.t("modifierType:ModifierType.EnemyAttackStatusEffectChanceModifierType.description", {
chancePercent: this.chancePercent, chancePercent: this.chancePercent,
statusEffect: getStatusEffectDescriptor(this.effect), statusEffect: getStatusEffectDescriptor(this.effect),
@ -1268,7 +1271,7 @@ export class EnemyEndureChanceModifierType extends ModifierType {
this.chancePercent = chancePercent; this.chancePercent = chancePercent;
} }
getDescription(scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.EnemyEndureChanceModifierType.description", { chancePercent: this.chancePercent }); return i18next.t("modifierType:ModifierType.EnemyEndureChanceModifierType.description", { chancePercent: this.chancePercent });
} }
} }
@ -1284,9 +1287,9 @@ type WeightedModifierTypeWeightFunc = (party: Pokemon[], rerollCount?: integer)
* @returns A WeightedModifierTypeWeightFunc * @returns A WeightedModifierTypeWeightFunc
*/ */
function skipInClassicAfterWave(wave: integer, defaultWeight: integer): WeightedModifierTypeWeightFunc { function skipInClassicAfterWave(wave: integer, defaultWeight: integer): WeightedModifierTypeWeightFunc {
return (party: Pokemon[]) => { return () => {
const gameMode = party[0].scene.gameMode; const gameMode = globalScene.gameMode;
const currentWave = party[0].scene.currentBattle.waveIndex; const currentWave = globalScene.currentBattle.waveIndex;
return gameMode.isClassic && currentWave >= wave ? 0 : defaultWeight; return gameMode.isClassic && currentWave >= wave ? 0 : defaultWeight;
}; };
} }
@ -1309,9 +1312,9 @@ function skipInLastClassicWaveOrDefault(defaultWeight: integer) : WeightedModifi
* @returns A WeightedModifierTypeWeightFunc * @returns A WeightedModifierTypeWeightFunc
*/ */
function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc { function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc {
return (party: Pokemon[]) => { return () => {
const lures = party[0].scene.getModifiers(DoubleBattleChanceBoosterModifier); const lures = globalScene.getModifiers(DoubleBattleChanceBoosterModifier);
return !(party[0].scene.gameMode.isClassic && party[0].scene.currentBattle.waveIndex === 199) && (lures.length === 0 || lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0) ? weight : 0; return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) && (lures.length === 0 || lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0) ? weight : 0;
}; };
} }
class WeightedModifierType { class WeightedModifierType {
@ -1446,7 +1449,7 @@ export const modifierTypes = {
TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterModifierTypeGenerator(), TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterModifierTypeGenerator(),
DIRE_HIT: () => new class extends ModifierType { DIRE_HIT: () => new class extends ModifierType {
getDescription(_scene: BattleScene): string { getDescription(): string {
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"), stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage") amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage")
@ -1458,7 +1461,7 @@ export const modifierTypes = {
ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterModifierTypeGenerator(), ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterModifierTypeGenerator(),
MINT: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MINT: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Nature)) { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Nature)) {
return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature); return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature);
} }
@ -1469,7 +1472,7 @@ export const modifierTypes = {
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Type)) { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Type)) {
return new TerastallizeModifierType(pregenArgs[0] as Type); return new TerastallizeModifierType(pregenArgs[0] as Type);
} }
if (!party[0].scene.getModifiers(TerastallizeAccessModifier).length) { if (!globalScene.getModifiers(TerastallizeAccessModifier).length) {
return null; return null;
} }
let type: Type; let type: Type;
@ -1482,7 +1485,7 @@ export const modifierTypes = {
return new TerastallizeModifierType(type); return new TerastallizeModifierType(type);
}), }),
BERRY: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { BERRY: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in BerryType)) { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in BerryType)) {
return new BerryModifierType(pregenArgs[0] as BerryType); return new BerryModifierType(pregenArgs[0] as BerryType);
} }
@ -1590,19 +1593,19 @@ export const modifierTypes = {
ENEMY_ENDURE_CHANCE: () => new EnemyEndureChanceModifierType("modifierType:ModifierType.ENEMY_ENDURE_CHANCE", "wl_reset_urge", 2), ENEMY_ENDURE_CHANCE: () => new EnemyEndureChanceModifierType("modifierType:ModifierType.ENEMY_ENDURE_CHANCE", "wl_reset_urge", 2),
ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new EnemyFusionChanceModifier(type, 1)), ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new EnemyFusionChanceModifier(type, 1)),
MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
return new PokemonBaseStatTotalModifierType(pregenArgs[0] as number); return new PokemonBaseStatTotalModifierType(pregenArgs[0] as number);
} }
return new PokemonBaseStatTotalModifierType(randSeedInt(20, 1)); return new PokemonBaseStatTotalModifierType(randSeedInt(20, 1));
}), }),
MYSTERY_ENCOUNTER_OLD_GATEAU: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MYSTERY_ENCOUNTER_OLD_GATEAU: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
return new PokemonBaseStatFlatModifierType(pregenArgs[0] as number, pregenArgs[1] as Stat[]); return new PokemonBaseStatFlatModifierType(pregenArgs[0] as number, pregenArgs[1] as Stat[]);
} }
return new PokemonBaseStatFlatModifierType(randSeedInt(20, 1), [ Stat.HP, Stat.ATK, Stat.DEF ]); return new PokemonBaseStatFlatModifierType(randSeedInt(20, 1), [ Stat.HP, Stat.ATK, Stat.DEF ]);
}), }),
MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new HealShopCostModifier(type, pregenArgs[0] as number)); return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new HealShopCostModifier(type, pregenArgs[0] as number));
} }
@ -1618,17 +1621,16 @@ interface ModifierPool {
/** /**
* Used to check if the player has max of a given ball type in Classic * Used to check if the player has max of a given ball type in Classic
* @param party The player's party, just used to access the scene
* @param ballType The {@linkcode PokeballType} being checked * @param ballType The {@linkcode PokeballType} being checked
* @returns boolean: true if the player has the maximum of a given ball type * @returns boolean: true if the player has the maximum of a given ball type
*/ */
function hasMaximumBalls(party: Pokemon[], ballType: PokeballType): boolean { function hasMaximumBalls(ballType: PokeballType): boolean {
return (party[0].scene.gameMode.isClassic && party[0].scene.pokeballCounts[ballType] >= MAX_PER_TYPE_POKEBALLS); return (globalScene.gameMode.isClassic && globalScene.pokeballCounts[ballType] >= MAX_PER_TYPE_POKEBALLS);
} }
const modifierPool: ModifierPool = { const modifierPool: ModifierPool = {
[ModifierTier.COMMON]: [ [ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.POKEBALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.POKEBALL)) ? 0 : 6, 6), new WeightedModifierType(modifierTypes.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL)) ? 0 : 6, 6),
new WeightedModifierType(modifierTypes.RARE_CANDY, 2), new WeightedModifierType(modifierTypes.RARE_CANDY, 2),
new WeightedModifierType(modifierTypes.POTION, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.POTION, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => (p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875) && !p.isFainted()).length, 3); const thresholdPartyMemberCount = Math.min(party.filter(p => (p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875) && !p.isFainted()).length, 3);
@ -1656,7 +1658,7 @@ const modifierPool: ModifierPool = {
m.setTier(ModifierTier.COMMON); return m; m.setTier(ModifierTier.COMMON); return m;
}), }),
[ModifierTier.GREAT]: [ [ModifierTier.GREAT]: [
new WeightedModifierType(modifierTypes.GREAT_BALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.GREAT_BALL)) ? 0 : 6, 6), new WeightedModifierType(modifierTypes.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL)) ? 0 : 6, 6),
new WeightedModifierType(modifierTypes.PP_UP, 2), new WeightedModifierType(modifierTypes.PP_UP, 2),
new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => { const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
@ -1710,10 +1712,10 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(15, 4)), new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(15, 4)),
new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)), new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)),
new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4), new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4),
new WeightedModifierType(modifierTypes.EVOLUTION_ITEM, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.EVOLUTION_ITEM, () => {
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15), 8); return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8);
}, 8), }, 8),
new WeightedModifierType(modifierTypes.MAP, (party: Pokemon[]) => party[0].scene.gameMode.isClassic && party[0].scene.currentBattle.waveIndex < 180 ? 2 : 0, 2), new WeightedModifierType(modifierTypes.MAP, () => globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0, 2),
new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2), new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2),
new WeightedModifierType(modifierTypes.TM_GREAT, 3), new WeightedModifierType(modifierTypes.TM_GREAT, 3),
new WeightedModifierType(modifierTypes.MEMORY_MUSHROOM, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.MEMORY_MUSHROOM, (party: Pokemon[]) => {
@ -1725,22 +1727,22 @@ const modifierPool: ModifierPool = {
}, 4), }, 4),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.TERA_SHARD, 1), new WeightedModifierType(modifierTypes.TERA_SHARD, 1),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => globalScene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0),
new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1), new WeightedModifierType(modifierTypes.VOUCHER, (_party: Pokemon[], rerollCount: integer) => !globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1),
].map(m => { ].map(m => {
m.setTier(ModifierTier.GREAT); return m; m.setTier(ModifierTier.GREAT); return m;
}), }),
[ModifierTier.ULTRA]: [ [ModifierTier.ULTRA]: [
new WeightedModifierType(modifierTypes.ULTRA_BALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.ULTRA_BALL)) ? 0 : 15, 15), new WeightedModifierType(modifierTypes.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL)) ? 0 : 15, 15),
new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(30, 4)), new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(30, 4)),
new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)),
new WeightedModifierType(modifierTypes.PP_MAX, 3), new WeightedModifierType(modifierTypes.PP_MAX, 3),
new WeightedModifierType(modifierTypes.MINT, 4), new WeightedModifierType(modifierTypes.MINT, 4),
new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32), new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32), 32),
new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, 24),
new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)),
new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => {
const { gameMode, gameData } = party[0].scene; const { gameMode, gameData } = globalScene;
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
return party.some(p => { return party.some(p => {
// Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd // Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd
@ -1847,14 +1849,14 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)),
new WeightedModifierType(modifierTypes.EXP_BALANCE, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EXP_BALANCE, skipInLastClassicWaveOrDefault(3)),
new WeightedModifierType(modifierTypes.TERA_ORB, (party: Pokemon[]) => Math.min(Math.max(Math.floor(party[0].scene.currentBattle.waveIndex / 50) * 2, 1), 4), 4), new WeightedModifierType(modifierTypes.TERA_ORB, () => Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4), 4),
new WeightedModifierType(modifierTypes.QUICK_CLAW, 3), new WeightedModifierType(modifierTypes.QUICK_CLAW, 3),
new WeightedModifierType(modifierTypes.WIDE_LENS, 4), new WeightedModifierType(modifierTypes.WIDE_LENS, 4),
].map(m => { ].map(m => {
m.setTier(ModifierTier.ULTRA); return m; m.setTier(ModifierTier.ULTRA); return m;
}), }),
[ModifierTier.ROGUE]: [ [ModifierTier.ROGUE]: [
new WeightedModifierType(modifierTypes.ROGUE_BALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.ROGUE_BALL)) ? 0 : 16, 16), new WeightedModifierType(modifierTypes.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL)) ? 0 : 16, 16),
new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)),
new WeightedModifierType(modifierTypes.LEFTOVERS, 3), new WeightedModifierType(modifierTypes.LEFTOVERS, 3),
new WeightedModifierType(modifierTypes.SHELL_BELL, 3), new WeightedModifierType(modifierTypes.SHELL_BELL, 3),
@ -1864,28 +1866,28 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.BATON, 2), new WeightedModifierType(modifierTypes.BATON, 2),
new WeightedModifierType(modifierTypes.SOUL_DEW, 7), new WeightedModifierType(modifierTypes.SOUL_DEW, 7),
//new WeightedModifierType(modifierTypes.OVAL_CHARM, 6), //new WeightedModifierType(modifierTypes.OVAL_CHARM, 6),
new WeightedModifierType(modifierTypes.CATCHING_CHARM, (party: Pokemon[]) => party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.getSpeciesCount(d => !!d.caughtAttr) > 100) ? 4 : 0, 4), new WeightedModifierType(modifierTypes.CATCHING_CHARM, () => globalScene.gameMode.isDaily || (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr) > 100) ? 4 : 0, 4),
new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)),
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, (party: Pokemon[]) => party[0].scene.gameMode.isClassic ? 0 : 3), new WeightedModifierType(modifierTypes.LOCK_CAPSULE, () => globalScene.gameMode.isClassic ? 0 : 3),
new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, 24),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.MEGA_BRACELET, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, 36),
new WeightedModifierType(modifierTypes.DYNAMAX_BAND, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36), new WeightedModifierType(modifierTypes.DYNAMAX_BAND, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, 36),
new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3), new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (_party: Pokemon[], rerollCount: integer) => !globalScene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3),
].map(m => { ].map(m => {
m.setTier(ModifierTier.ROGUE); return m; m.setTier(ModifierTier.ROGUE); return m;
}), }),
[ModifierTier.MASTER]: [ [ModifierTier.MASTER]: [
new WeightedModifierType(modifierTypes.MASTER_BALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.MASTER_BALL)) ? 0 : 24, 24), new WeightedModifierType(modifierTypes.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL)) ? 0 : 24, 24),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 14), new WeightedModifierType(modifierTypes.SHINY_CHARM, 14),
new WeightedModifierType(modifierTypes.HEALING_CHARM, 18), new WeightedModifierType(modifierTypes.HEALING_CHARM, 18),
new WeightedModifierType(modifierTypes.MULTI_LENS, 18), new WeightedModifierType(modifierTypes.MULTI_LENS, 18),
new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (_party: Pokemon[], rerollCount: integer) =>
!party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5), !globalScene.gameMode.isDaily && !globalScene.gameMode.isEndless && !globalScene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !globalScene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))) ? 1 : 0, 1), new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, () => (globalScene.gameMode.isDaily || (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))) ? 1 : 0, 1),
].map(m => { ].map(m => {
m.setTier(ModifierTier.MASTER); return m; m.setTier(ModifierTier.MASTER); return m;
}) })
@ -2084,7 +2086,7 @@ export const itemPoolChecks: Map<ModifierTypeKeys, boolean | undefined> = new Ma
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) { export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) {
const pool = getModifierPoolForType(poolType); const pool = getModifierPoolForType(poolType);
itemPoolChecks.forEach((v, k) => { itemPoolChecks.forEach((_v, k) => {
itemPoolChecks.set(k, false); itemPoolChecks.set(k, false);
}); });
@ -2098,14 +2100,14 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
let i = 0; let i = 0;
pool[t].reduce((total: integer, modifierType: WeightedModifierType) => { pool[t].reduce((total: integer, modifierType: WeightedModifierType) => {
const weightedModifierType = modifierType as WeightedModifierType; const weightedModifierType = modifierType as WeightedModifierType;
const existingModifiers = party[0].scene.findModifiers(m => m.type.id === weightedModifierType.modifierType.id, poolType === ModifierPoolType.PLAYER); const existingModifiers = globalScene.findModifiers(m => m.type.id === weightedModifierType.modifierType.id, poolType === ModifierPoolType.PLAYER);
const itemModifierType = weightedModifierType.modifierType instanceof ModifierTypeGenerator const itemModifierType = weightedModifierType.modifierType instanceof ModifierTypeGenerator
? weightedModifierType.modifierType.generateType(party) ? weightedModifierType.modifierType.generateType(party)
: weightedModifierType.modifierType; : weightedModifierType.modifierType;
const weight = !existingModifiers.length const weight = !existingModifiers.length
|| itemModifierType instanceof PokemonHeldItemModifierType || itemModifierType instanceof PokemonHeldItemModifierType
|| itemModifierType instanceof FormChangeItemModifierType || itemModifierType instanceof FormChangeItemModifierType
|| existingModifiers.find(m => m.stackCount < m.getMaxStackCount(party[0].scene, true)) || existingModifiers.find(m => m.stackCount < m.getMaxStackCount(true))
? weightedModifierType.weight instanceof Function ? weightedModifierType.weight instanceof Function
? (weightedModifierType.weight as Function)(party, rerollCount) ? (weightedModifierType.weight as Function)(party, rerollCount)
: weightedModifierType.weight as integer : weightedModifierType.weight as integer
@ -2207,7 +2209,7 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo
// Guaranteed mod functions second // Guaranteed mod functions second
if (customModifierSettings.guaranteedModifierTypeFuncs && customModifierSettings.guaranteedModifierTypeFuncs.length > 0) { if (customModifierSettings.guaranteedModifierTypeFuncs && customModifierSettings.guaranteedModifierTypeFuncs.length > 0) {
customModifierSettings.guaranteedModifierTypeFuncs!.forEach((mod, i) => { customModifierSettings.guaranteedModifierTypeFuncs!.forEach((mod, _i) => {
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === mod) as string; const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === mod) as string;
let guaranteedMod: ModifierType = modifierTypes[modifierId]?.(); let guaranteedMod: ModifierType = modifierTypes[modifierId]?.();
@ -2326,7 +2328,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base
return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat();
} }
export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: PersistentModifier[], scene: BattleScene): EnemyPersistentModifier { export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: PersistentModifier[]): EnemyPersistentModifier {
let tierStackCount: number; let tierStackCount: number;
switch (tier) { switch (tier) {
case ModifierTier.ULTRA: case ModifierTier.ULTRA:
@ -2344,7 +2346,7 @@ export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers:
let candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); let candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier);
let r = 0; let r = 0;
let matchingModifier: PersistentModifier | undefined; let matchingModifier: PersistentModifier | undefined;
while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) && matchingModifier.getMaxStackCount() < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) {
candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier);
} }
@ -2535,11 +2537,11 @@ export class ModifierTypeOption {
* @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode. * @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode.
*/ */
export function getPartyLuckValue(party: Pokemon[]): integer { export function getPartyLuckValue(party: Pokemon[]): integer {
if (party[0].scene.gameMode.isDaily) { if (globalScene.gameMode.isDaily) {
const DailyLuck = new NumberHolder(0); const DailyLuck = new NumberHolder(0);
party[0].scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
DailyLuck.value = randSeedInt(15); // Random number between 0 and 14 DailyLuck.value = randSeedInt(15); // Random number between 0 and 14
}, 0, party[0].scene.seed); }, 0, globalScene.seed);
return DailyLuck.value; return DailyLuck.value;
} }
const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0)

View File

@ -1,4 +1,3 @@
import type BattleScene from "#app/battle-scene";
import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
import { getLevelTotalExp } from "#app/data/exp"; import { getLevelTotalExp } from "#app/data/exp";
@ -20,7 +19,7 @@ import { addTextObject, TextStyle } from "#app/ui/text";
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils"; import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import type { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
import type { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -32,6 +31,7 @@ import { type DoubleBattleChanceBoosterModifierType, type EvolutionItemModifierT
import { Color, ShadowColor } from "#enums/color"; import { Color, ShadowColor } from "#enums/color";
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters"; import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters";
import { applyAbAttrs, CommanderAbAttr } from "#app/data/ability"; import { applyAbAttrs, CommanderAbAttr } from "#app/data/ability";
import { globalScene } from "#app/global-scene";
export type ModifierPredicate = (modifier: Modifier) => boolean; export type ModifierPredicate = (modifier: Modifier) => boolean;
@ -65,8 +65,8 @@ export class ModifierBar extends Phaser.GameObjects.Container {
private player: boolean; private player: boolean;
private modifierCache: PersistentModifier[]; private modifierCache: PersistentModifier[];
constructor(scene: BattleScene, enemy?: boolean) { constructor(enemy?: boolean) {
super(scene, 1 + (enemy ? 302 : 0), 2); super(globalScene, 1 + (enemy ? 302 : 0), 2);
this.player = !enemy; this.player = !enemy;
this.setScale(0.5); this.setScale(0.5);
@ -80,7 +80,7 @@ export class ModifierBar extends Phaser.GameObjects.Container {
updateModifiers(modifiers: PersistentModifier[], hideHeldItems: boolean = false) { updateModifiers(modifiers: PersistentModifier[], hideHeldItems: boolean = false) {
this.removeAll(true); this.removeAll(true);
const visibleIconModifiers = modifiers.filter(m => m.isIconVisible(this.scene as BattleScene)); const visibleIconModifiers = modifiers.filter(m => m.isIconVisible());
const nonPokemonSpecificModifiers = visibleIconModifiers.filter(m => !(m as PokemonHeldItemModifier).pokemonId).sort(modifierSortFunc); const nonPokemonSpecificModifiers = visibleIconModifiers.filter(m => !(m as PokemonHeldItemModifier).pokemonId).sort(modifierSortFunc);
const pokemonSpecificModifiers = visibleIconModifiers.filter(m => (m as PokemonHeldItemModifier).pokemonId).sort(modifierSortFunc); const pokemonSpecificModifiers = visibleIconModifiers.filter(m => (m as PokemonHeldItemModifier).pokemonId).sort(modifierSortFunc);
@ -89,7 +89,7 @@ export class ModifierBar extends Phaser.GameObjects.Container {
const thisArg = this; const thisArg = this;
sortedVisibleIconModifiers.forEach((modifier: PersistentModifier, i: number) => { sortedVisibleIconModifiers.forEach((modifier: PersistentModifier, i: number) => {
const icon = modifier.getIcon(this.scene as BattleScene); const icon = modifier.getIcon();
if (i >= iconOverflowIndex) { if (i >= iconOverflowIndex) {
icon.setVisible(false); icon.setVisible(false);
} }
@ -97,13 +97,13 @@ export class ModifierBar extends Phaser.GameObjects.Container {
this.setModifierIconPosition(icon, sortedVisibleIconModifiers.length); this.setModifierIconPosition(icon, sortedVisibleIconModifiers.length);
icon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 32, 24), Phaser.Geom.Rectangle.Contains); icon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 32, 24), Phaser.Geom.Rectangle.Contains);
icon.on("pointerover", () => { icon.on("pointerover", () => {
(this.scene as BattleScene).ui.showTooltip(modifier.type.name, modifier.type.getDescription(this.scene as BattleScene)); globalScene.ui.showTooltip(modifier.type.name, modifier.type.getDescription());
if (this.modifierCache && this.modifierCache.length > iconOverflowIndex) { if (this.modifierCache && this.modifierCache.length > iconOverflowIndex) {
thisArg.updateModifierOverflowVisibility(true); thisArg.updateModifierOverflowVisibility(true);
} }
}); });
icon.on("pointerout", () => { icon.on("pointerout", () => {
(this.scene as BattleScene).ui.hideTooltip(); globalScene.ui.hideTooltip();
if (this.modifierCache && this.modifierCache.length > iconOverflowIndex) { if (this.modifierCache && this.modifierCache.length > iconOverflowIndex) {
thisArg.updateModifierOverflowVisibility(false); thisArg.updateModifierOverflowVisibility(false);
} }
@ -171,10 +171,10 @@ export abstract class PersistentModifier extends Modifier {
this.virtualStackCount = 0; this.virtualStackCount = 0;
} }
add(modifiers: PersistentModifier[], virtual: boolean, scene: BattleScene): boolean { add(modifiers: PersistentModifier[], virtual: boolean): boolean {
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (this.match(modifier)) { if (this.match(modifier)) {
return modifier.incrementStack(scene, this.stackCount, virtual); return modifier.incrementStack(this.stackCount, virtual);
} }
} }
@ -192,8 +192,8 @@ export abstract class PersistentModifier extends Modifier {
return []; return [];
} }
incrementStack(scene: BattleScene, amount: number, virtual: boolean): boolean { incrementStack(amount: number, virtual: boolean): boolean {
if (this.getStackCount() + amount <= this.getMaxStackCount(scene)) { if (this.getStackCount() + amount <= this.getMaxStackCount()) {
if (!virtual) { if (!virtual) {
this.stackCount += amount; this.stackCount += amount;
} else { } else {
@ -209,26 +209,26 @@ export abstract class PersistentModifier extends Modifier {
return this.stackCount + this.virtualStackCount; return this.stackCount + this.virtualStackCount;
} }
abstract getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number; abstract getMaxStackCount(forThreshold?: boolean): number;
isIconVisible(scene: BattleScene): boolean { isIconVisible(): boolean {
return true; return true;
} }
getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { getIcon(forSummary?: boolean): Phaser.GameObjects.Container {
const container = scene.add.container(0, 0); const container = globalScene.add.container(0, 0);
const item = scene.add.sprite(0, 12, "items"); const item = globalScene.add.sprite(0, 12, "items");
item.setFrame(this.type.iconImage); item.setFrame(this.type.iconImage);
item.setOrigin(0, 0.5); item.setOrigin(0, 0.5);
container.add(item); container.add(item);
const stackText = this.getIconStackText(scene); const stackText = this.getIconStackText();
if (stackText) { if (stackText) {
container.add(stackText); container.add(stackText);
} }
const virtualStackText = this.getIconStackText(scene, true); const virtualStackText = this.getIconStackText(true);
if (virtualStackText) { if (virtualStackText) {
container.add(virtualStackText); container.add(virtualStackText);
} }
@ -236,14 +236,14 @@ export abstract class PersistentModifier extends Modifier {
return container; return container;
} }
getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText | null { getIconStackText(virtual?: boolean): Phaser.GameObjects.BitmapText | null {
if (this.getMaxStackCount(scene) === 1 || (virtual && !this.virtualStackCount)) { if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount)) {
return null; return null;
} }
const text = scene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11); const text = globalScene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11);
text.letterSpacing = -0.5; text.letterSpacing = -0.5;
if (this.getStackCount() >= this.getMaxStackCount(scene)) { if (this.getStackCount() >= this.getMaxStackCount()) {
text.setTint(0xf89890); text.setTint(0xf89890);
} }
text.setOrigin(0, 0); text.setOrigin(0, 0);
@ -278,8 +278,8 @@ export class AddPokeballModifier extends ConsumableModifier {
* @param battleScene {@linkcode BattleScene} * @param battleScene {@linkcode BattleScene}
* @returns always `true` * @returns always `true`
*/ */
override apply(battleScene: BattleScene): boolean { override apply(): boolean {
const pokeballCounts = battleScene.pokeballCounts; const pokeballCounts = globalScene.pokeballCounts;
pokeballCounts[this.pokeballType] = Math.min(pokeballCounts[this.pokeballType] + this.count, MAX_PER_TYPE_POKEBALLS); pokeballCounts[this.pokeballType] = Math.min(pokeballCounts[this.pokeballType] + this.count, MAX_PER_TYPE_POKEBALLS);
return true; return true;
@ -302,8 +302,8 @@ export class AddVoucherModifier extends ConsumableModifier {
* @param battleScene {@linkcode BattleScene} * @param battleScene {@linkcode BattleScene}
* @returns always `true` * @returns always `true`
*/ */
override apply(battleScene: BattleScene): boolean { override apply(): boolean {
const voucherCounts = battleScene.gameData.voucherCounts; const voucherCounts = globalScene.gameData.voucherCounts;
voucherCounts[this.voucherType] += this.count; voucherCounts[this.voucherType] += this.count;
return true; return true;
@ -343,13 +343,13 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
* @param _scene N/A * @param _scene N/A
* @returns `true` if the modifier was successfully added or applied, false otherwise * @returns `true` if the modifier was successfully added or applied, false otherwise
*/ */
add(modifiers: PersistentModifier[], _virtual: boolean, scene: BattleScene): boolean { add(modifiers: PersistentModifier[], _virtual: boolean): boolean {
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (this.match(modifier)) { if (this.match(modifier)) {
const modifierInstance = modifier as LapsingPersistentModifier; const modifierInstance = modifier as LapsingPersistentModifier;
if (modifierInstance.getBattleCount() < modifierInstance.getMaxBattles()) { if (modifierInstance.getBattleCount() < modifierInstance.getMaxBattles()) {
modifierInstance.resetBattleCount(); modifierInstance.resetBattleCount();
scene.playSound("se/restore"); globalScene.playSound("se/restore");
return true; return true;
} }
// should never get here // should never get here
@ -371,8 +371,8 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
return this.battleCount > 0; return this.battleCount > 0;
} }
getIcon(scene: BattleScene): Phaser.GameObjects.Container { getIcon(): Phaser.GameObjects.Container {
const container = super.getIcon(scene); const container = super.getIcon();
// Linear interpolation on hue // Linear interpolation on hue
const hue = Math.floor(120 * (this.battleCount / this.maxBattles) + 5); const hue = Math.floor(120 * (this.battleCount / this.maxBattles) + 5);
@ -381,7 +381,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
const typeHex = hslToHex(hue, 0.5, 0.9); const typeHex = hslToHex(hue, 0.5, 0.9);
const strokeHex = hslToHex(hue, 0.7, 0.3); const strokeHex = hslToHex(hue, 0.7, 0.3);
const battleCountText = addTextObject(scene, 27, 0, this.battleCount.toString(), TextStyle.PARTY, { const battleCountText = addTextObject(27, 0, this.battleCount.toString(), TextStyle.PARTY, {
fontSize: "66px", fontSize: "66px",
color: typeHex, color: typeHex,
}); });
@ -393,7 +393,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
return container; return container;
} }
getIconStackText(_scene: BattleScene, _virtual?: boolean): Phaser.GameObjects.BitmapText | null { getIconStackText(_virtual?: boolean): Phaser.GameObjects.BitmapText | null {
return null; return null;
} }
@ -421,7 +421,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier {
return [ this.maxBattles, this.battleCount ]; return [ this.maxBattles, this.battleCount ];
} }
getMaxStackCount(_scene: BattleScene, _forThreshold?: boolean): number { getMaxStackCount(_forThreshold?: boolean): number {
// Must be an abitrary number greater than 1 // Must be an abitrary number greater than 1
return 2; return 2;
} }
@ -574,7 +574,7 @@ export class MapModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -592,7 +592,7 @@ export class MegaEvolutionAccessModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -615,7 +615,7 @@ export class GigantamaxAccessModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -638,7 +638,7 @@ export class TerastallizeAccessModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -680,33 +680,33 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
return !!pokemon && (this.pokemonId === -1 || pokemon.id === this.pokemonId); return !!pokemon && (this.pokemonId === -1 || pokemon.id === this.pokemonId);
} }
isIconVisible(scene: BattleScene): boolean { isIconVisible(): boolean {
return !!(this.getPokemon(scene)?.isOnField()); return !!(this.getPokemon()?.isOnField());
} }
getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { getIcon(forSummary?: boolean): Phaser.GameObjects.Container {
const container = !forSummary ? scene.add.container(0, 0) : super.getIcon(scene); const container = !forSummary ? globalScene.add.container(0, 0) : super.getIcon();
if (!forSummary) { if (!forSummary) {
const pokemon = this.getPokemon(scene); const pokemon = this.getPokemon();
if (pokemon) { if (pokemon) {
const pokemonIcon = scene.addPokemonIcon(pokemon, -2, 10, 0, 0.5); const pokemonIcon = globalScene.addPokemonIcon(pokemon, -2, 10, 0, 0.5);
container.add(pokemonIcon); container.add(pokemonIcon);
container.setName(pokemon.id.toString()); container.setName(pokemon.id.toString());
} }
const item = scene.add.sprite(16, this.virtualStackCount ? 8 : 16, "items"); const item = globalScene.add.sprite(16, this.virtualStackCount ? 8 : 16, "items");
item.setScale(0.5); item.setScale(0.5);
item.setOrigin(0, 0.5); item.setOrigin(0, 0.5);
item.setTexture("items", this.type.iconImage); item.setTexture("items", this.type.iconImage);
container.add(item); container.add(item);
const stackText = this.getIconStackText(scene); const stackText = this.getIconStackText();
if (stackText) { if (stackText) {
container.add(stackText); container.add(stackText);
} }
const virtualStackText = this.getIconStackText(scene, true); const virtualStackText = this.getIconStackText(true);
if (virtualStackText) { if (virtualStackText) {
container.add(virtualStackText); container.add(virtualStackText);
} }
@ -717,21 +717,21 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
return container; return container;
} }
getPokemon(scene: BattleScene): Pokemon | undefined { getPokemon(): Pokemon | undefined {
return this.pokemonId ? scene.getPokemonById(this.pokemonId) ?? undefined : undefined; return this.pokemonId ? globalScene.getPokemonById(this.pokemonId) ?? undefined : undefined;
} }
getScoreMultiplier(): number { getScoreMultiplier(): number {
return 1; return 1;
} }
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { getMaxStackCount(forThreshold?: boolean): number {
const pokemon = this.getPokemon(scene); const pokemon = this.getPokemon();
if (!pokemon) { if (!pokemon) {
return 0; return 0;
} }
if (pokemon.isPlayer() && forThreshold) { if (pokemon.isPlayer() && forThreshold) {
return scene.getPlayerParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: number, maxStackCount: number) => Math.max(stackCount, maxStackCount), 0); return globalScene.getPlayerParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: number, maxStackCount: number) => Math.max(stackCount, maxStackCount), 0);
} }
return this.getMaxHeldItemCount(pokemon); return this.getMaxHeldItemCount(pokemon);
} }
@ -760,15 +760,14 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi
/** /**
* Retrieve the {@linkcode Modifier | Modifiers} icon as a {@linkcode Phaser.GameObjects.Container | Container} * Retrieve the {@linkcode Modifier | Modifiers} icon as a {@linkcode Phaser.GameObjects.Container | Container}
* @param scene The {@linkcode BattleScene}
* @param forSummary `true` if the icon is for the summary screen * @param forSummary `true` if the icon is for the summary screen
* @returns the icon as a {@linkcode Phaser.GameObjects.Container | Container} * @returns the icon as a {@linkcode Phaser.GameObjects.Container | Container}
*/ */
public getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { public getIcon(forSummary?: boolean): Phaser.GameObjects.Container {
const container = super.getIcon(scene, forSummary); const container = super.getIcon(forSummary);
if (this.getPokemon(scene)?.isPlayer()) { if (this.getPokemon()?.isPlayer()) {
const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: Color.PINK }); const battleCountText = addTextObject(27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: Color.PINK });
battleCountText.setShadow(0, 0); battleCountText.setShadow(0, 0);
battleCountText.setStroke(ShadowColor.RED, 16); battleCountText.setStroke(ShadowColor.RED, 16);
battleCountText.setOrigin(1, 0); battleCountText.setOrigin(1, 0);
@ -782,7 +781,7 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi
return this.battlesLeft; return this.battlesLeft;
} }
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { getMaxStackCount(forThreshold?: boolean): number {
return 1; return 1;
} }
} }
@ -820,10 +819,10 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
*/ */
override apply(pokemon: Pokemon): boolean { override apply(pokemon: Pokemon): boolean {
if (pokemon.isPlayer()) { if (pokemon.isPlayer()) {
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeTeraTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeTeraTrigger);
pokemon.scene.validateAchv(achvs.TERASTALLIZE); globalScene.validateAchv(achvs.TERASTALLIZE);
if (this.teraType === Type.STELLAR) { if (this.teraType === Type.STELLAR) {
pokemon.scene.validateAchv(achvs.STELLAR_TERASTALLIZE); globalScene.validateAchv(achvs.STELLAR_TERASTALLIZE);
} }
} }
pokemon.updateSpritePipelineData(); pokemon.updateSpritePipelineData();
@ -838,7 +837,7 @@ export class TerastallizeModifier extends LapsingPokemonHeldItemModifier {
public override lapse(pokemon: Pokemon): boolean { public override lapse(pokemon: Pokemon): boolean {
const ret = super.lapse(pokemon); const ret = super.lapse(pokemon);
if (!ret) { if (!ret) {
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeLapseTeraTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeLapseTeraTrigger);
pokemon.updateSpritePipelineData(); pokemon.updateSpritePipelineData();
} }
return ret; return ret;
@ -944,19 +943,19 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier {
return true; return true;
} }
getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText | null { getIconStackText(virtual?: boolean): Phaser.GameObjects.BitmapText | null {
if (this.getMaxStackCount(scene) === 1 || (virtual && !this.virtualStackCount)) { if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount)) {
return null; return null;
} }
const pokemon = scene.getPokemonById(this.pokemonId); const pokemon = globalScene.getPokemonById(this.pokemonId);
this.stackCount = pokemon this.stackCount = pokemon
? pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length ? pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ pokemon.scene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length + globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length
: this.stackCount; : this.stackCount;
const text = scene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11); const text = globalScene.add.bitmapText(10, 15, "item-count", this.stackCount.toString(), 11);
text.letterSpacing = -0.5; text.letterSpacing = -0.5;
if (this.getStackCount() >= this.required) { if (this.getStackCount() >= this.required) {
text.setTint(0xf89890); text.setTint(0xf89890);
@ -968,7 +967,7 @@ export class EvoTrackerModifier extends PokemonHeldItemModifier {
getMaxHeldItemCount(pokemon: Pokemon): number { getMaxHeldItemCount(pokemon: Pokemon): number {
this.stackCount = pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length this.stackCount = pokemon.evoCounter + pokemon.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ pokemon.scene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length; + globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier || m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length;
return 999; return 999;
} }
} }
@ -1536,7 +1535,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) {
surviveDamage.value = true; surviveDamage.value = true;
pokemon.scene.queueMessage(i18next.t("modifier:surviveDamageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name })); globalScene.queueMessage(i18next.t("modifier:surviveDamageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }));
return true; return true;
} }
@ -1580,11 +1579,11 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier {
override apply(pokemon: Pokemon, doBypassSpeed: BooleanHolder): boolean { override apply(pokemon: Pokemon, doBypassSpeed: BooleanHolder): boolean {
if (!doBypassSpeed.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!doBypassSpeed.value && pokemon.randSeedInt(10) < this.getStackCount()) {
doBypassSpeed.value = true; doBypassSpeed.value = true;
const isCommandFight = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT; const isCommandFight = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]?.command === Command.FIGHT;
const hasQuickClaw = this.type instanceof PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW"; const hasQuickClaw = this.type instanceof PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW";
if (isCommandFight && hasQuickClaw) { if (isCommandFight && hasQuickClaw) {
pokemon.scene.queueMessage(i18next.t("modifier:bypassSpeedChanceApply", { pokemonName: getPokemonNameWithAffix(pokemon), itemName: i18next.t("modifierType:ModifierType.QUICK_CLAW.name") })); globalScene.queueMessage(i18next.t("modifier:bypassSpeedChanceApply", { pokemonName: getPokemonNameWithAffix(pokemon), itemName: i18next.t("modifierType:ModifierType.QUICK_CLAW.name") }));
} }
return true; return true;
} }
@ -1668,8 +1667,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
*/ */
override apply(pokemon: Pokemon): boolean { override apply(pokemon: Pokemon): boolean {
if (!pokemon.isFullHp()) { if (!pokemon.isFullHp()) {
const scene = pokemon.scene; globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(),
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
return true; return true;
} }
@ -1761,8 +1759,7 @@ export class HitHealModifier extends PokemonHeldItemModifier {
*/ */
override apply(pokemon: Pokemon): boolean { override apply(pokemon: Pokemon): boolean {
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) { if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
const scene = pokemon.scene; globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(),
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
} }
@ -1807,7 +1804,7 @@ export class LevelIncrementBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { getMaxStackCount(forThreshold?: boolean): number {
return 99; return 99;
} }
} }
@ -1851,7 +1848,7 @@ export class BerryModifier extends PokemonHeldItemModifier {
*/ */
override apply(pokemon: Pokemon): boolean { override apply(pokemon: Pokemon): boolean {
const preserve = new BooleanHolder(false); const preserve = new BooleanHolder(false);
pokemon.scene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve); globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve);
getBerryEffectFunc(this.berryType)(pokemon); getBerryEffectFunc(this.berryType)(pokemon);
if (!preserve.value) { if (!preserve.value) {
@ -1906,7 +1903,7 @@ export class PreserveBerryModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 3; return 3;
} }
} }
@ -1931,14 +1928,14 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
*/ */
override apply(pokemon: Pokemon): boolean { override apply(pokemon: Pokemon): boolean {
// Restore the Pokemon to half HP // Restore the Pokemon to half HP
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(),
toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true)); toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
// Remove the Pokemon's FAINT status // Remove the Pokemon's FAINT status
pokemon.resetStatus(true, false, true); pokemon.resetStatus(true, false, true);
// Reapply Commander on the Pokemon's side of the field, if applicable // Reapply Commander on the Pokemon's side of the field, if applicable
const field = pokemon.isPlayer() ? pokemon.scene.getPlayerField() : pokemon.scene.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));
return true; return true;
} }
@ -1984,7 +1981,7 @@ export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier {
} }
if (statRestored) { if (statRestored) {
pokemon.scene.queueMessage(i18next.t("modifier:resetNegativeStatStageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name })); globalScene.queueMessage(i18next.t("modifier:resetNegativeStatStageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }));
} }
return statRestored; return statRestored;
} }
@ -2020,8 +2017,8 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier {
*/ */
abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean | Promise<boolean>; abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean | Promise<boolean>;
getPokemon(scene: BattleScene) { getPokemon() {
return scene.getPlayerParty().find(p => p.id === this.pokemonId); return globalScene.getPlayerParty().find(p => p.id === this.pokemonId);
} }
} }
@ -2189,7 +2186,7 @@ export class PokemonNatureChangeModifier extends ConsumablePokemonModifier {
*/ */
override apply(playerPokemon: PlayerPokemon): boolean { override apply(playerPokemon: PlayerPokemon): boolean {
playerPokemon.setCustomNature(this.nature); playerPokemon.setCustomNature(this.nature);
playerPokemon.scene.gameData.unlockSpeciesNature(playerPokemon.species, this.nature); globalScene.gameData.unlockSpeciesNature(playerPokemon.species, this.nature);
return true; return true;
} }
@ -2207,17 +2204,17 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
* @returns always `true` * @returns always `true`
*/ */
override apply(playerPokemon: PlayerPokemon, levelCount: NumberHolder = new NumberHolder(1)): boolean { override apply(playerPokemon: PlayerPokemon, levelCount: NumberHolder = new NumberHolder(1)): boolean {
playerPokemon.scene.applyModifiers(LevelIncrementBoosterModifier, true, levelCount); globalScene.applyModifiers(LevelIncrementBoosterModifier, true, levelCount);
playerPokemon.level += levelCount.value; playerPokemon.level += levelCount.value;
if (playerPokemon.level <= playerPokemon.scene.getMaxExpLevel(true)) { if (playerPokemon.level <= globalScene.getMaxExpLevel(true)) {
playerPokemon.exp = getLevelTotalExp(playerPokemon.level, playerPokemon.species.growthRate); playerPokemon.exp = getLevelTotalExp(playerPokemon.level, playerPokemon.species.growthRate);
playerPokemon.levelExp = 0; playerPokemon.levelExp = 0;
} }
playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY); playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY);
playerPokemon.scene.unshiftPhase(new LevelUpPhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), playerPokemon.level - levelCount.value, playerPokemon.level)); globalScene.unshiftPhase(new LevelUpPhase(globalScene.getPlayerParty().indexOf(playerPokemon), playerPokemon.level - levelCount.value, playerPokemon.level));
return true; return true;
} }
@ -2237,7 +2234,7 @@ export class TmModifier extends ConsumablePokemonModifier {
*/ */
override apply(playerPokemon: PlayerPokemon): boolean { override apply(playerPokemon: PlayerPokemon): boolean {
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM)); globalScene.unshiftPhase(new LearnMovePhase(globalScene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM));
return true; return true;
} }
@ -2259,7 +2256,7 @@ export class RememberMoveModifier extends ConsumablePokemonModifier {
*/ */
override apply(playerPokemon: PlayerPokemon, cost?: number): boolean { override apply(playerPokemon: PlayerPokemon, cost?: number): boolean {
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], LearnMoveType.MEMORY, cost)); globalScene.unshiftPhase(new LearnMovePhase(globalScene.getPlayerParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], LearnMoveType.MEMORY, cost));
return true; return true;
} }
@ -2294,7 +2291,7 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier {
} }
if (matchingEvolution) { if (matchingEvolution) {
playerPokemon.scene.unshiftPhase(new EvolutionPhase(playerPokemon.scene, playerPokemon, matchingEvolution, playerPokemon.level - 1)); globalScene.unshiftPhase(new EvolutionPhase(playerPokemon, matchingEvolution, playerPokemon.level - 1));
return true; return true;
} }
@ -2354,7 +2351,7 @@ export class MultipleParticipantExpBonusModifier extends PersistentModifier {
return new MultipleParticipantExpBonusModifier(this.type, this.stackCount); return new MultipleParticipantExpBonusModifier(this.type, this.stackCount);
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -2391,7 +2388,7 @@ export class HealingBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -2432,7 +2429,7 @@ export class ExpBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number { getMaxStackCount(forThreshold?: boolean): number {
return this.boostMultiplier < 1 ? this.boostMultiplier < 0.6 ? 99 : 30 : 10; return this.boostMultiplier < 1 ? this.boostMultiplier < 0.6 ? 99 : 30 : 10;
} }
} }
@ -2511,7 +2508,7 @@ export class ExpShareModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -2537,7 +2534,7 @@ export class ExpBalanceModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 4; return 4;
} }
} }
@ -2774,7 +2771,7 @@ export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
this.active = false; this.active = false;
} }
const ret = pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger); const ret = globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger);
if (switchActive) { if (switchActive) {
this.active = true; this.active = true;
@ -2799,21 +2796,20 @@ export class MoneyRewardModifier extends ConsumableModifier {
/** /**
* Applies {@linkcode MoneyRewardModifier} * Applies {@linkcode MoneyRewardModifier}
* @param battleScene The current {@linkcode BattleScene}
* @returns always `true` * @returns always `true`
*/ */
override apply(battleScene: BattleScene): boolean { override apply(): boolean {
const moneyAmount = new NumberHolder(battleScene.getWaveMoneyAmount(this.moneyMultiplier)); const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
battleScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
battleScene.addMoney(moneyAmount.value); globalScene.addMoney(moneyAmount.value);
battleScene.getPlayerParty().map(p => { globalScene.getPlayerParty().map(p => {
if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) { if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) {
p.evoCounter ? p.evoCounter += Math.min(Math.floor(this.moneyMultiplier), 3) : p.evoCounter = Math.min(Math.floor(this.moneyMultiplier), 3); p.evoCounter ? p.evoCounter += Math.min(Math.floor(this.moneyMultiplier), 3) : p.evoCounter = Math.min(Math.floor(this.moneyMultiplier), 3);
const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier; const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier;
battleScene.addModifier(modifier); globalScene.addModifier(modifier);
} }
}); });
@ -2845,7 +2841,7 @@ export class MoneyMultiplierModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -2870,10 +2866,9 @@ export class DamageMoneyRewardModifier extends PokemonHeldItemModifier {
* @returns always `true` * @returns always `true`
*/ */
override apply(pokemon: Pokemon, multiplier: NumberHolder): boolean { override apply(pokemon: Pokemon, multiplier: NumberHolder): boolean {
const battleScene = pokemon.scene;
const moneyAmount = new NumberHolder(Math.floor(multiplier.value * (0.5 * this.getStackCount()))); const moneyAmount = new NumberHolder(Math.floor(multiplier.value * (0.5 * this.getStackCount())));
battleScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
battleScene.addMoney(moneyAmount.value); globalScene.addMoney(moneyAmount.value);
return true; return true;
} }
@ -2894,17 +2889,16 @@ export class MoneyInterestModifier extends PersistentModifier {
/** /**
* Applies {@linkcode MoneyInterestModifier} * Applies {@linkcode MoneyInterestModifier}
* @param battleScene The current {@linkcode BattleScene}
* @returns always `true` * @returns always `true`
*/ */
override apply(battleScene: BattleScene): boolean { override apply(): boolean {
const interestAmount = Math.floor(battleScene.money * 0.1 * this.getStackCount()); const interestAmount = Math.floor(globalScene.money * 0.1 * this.getStackCount());
battleScene.addMoney(interestAmount); globalScene.addMoney(interestAmount);
const userLocale = navigator.language || "en-US"; const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = interestAmount.toLocaleString(userLocale); const formattedMoneyAmount = interestAmount.toLocaleString(userLocale);
const message = i18next.t("modifier:moneyInterestApply", { moneyAmount: formattedMoneyAmount, typeName: this.type.name }); const message = i18next.t("modifier:moneyInterestApply", { moneyAmount: formattedMoneyAmount, typeName: this.type.name });
battleScene.queueMessage(message, undefined, true); globalScene.queueMessage(message, undefined, true);
return true; return true;
} }
@ -2913,7 +2907,7 @@ export class MoneyInterestModifier extends PersistentModifier {
return new MoneyInterestModifier(this.type, this.stackCount); return new MoneyInterestModifier(this.type, this.stackCount);
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -2942,7 +2936,7 @@ export class HiddenAbilityRateBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 4; return 4;
} }
} }
@ -2971,7 +2965,7 @@ export class ShinyRateBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 4; return 4;
} }
} }
@ -3003,7 +2997,7 @@ export class CriticalCatchChanceBoosterModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 3; return 3;
} }
} }
@ -3029,7 +3023,7 @@ export class LockModifierTiersModifier extends PersistentModifier {
return new LockModifierTiersModifier(this.type, this.stackCount); return new LockModifierTiersModifier(this.type, this.stackCount);
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -3069,7 +3063,7 @@ export class HealShopCostModifier extends PersistentModifier {
return super.getArgs().concat(this.shopMultiplier); return super.getArgs().concat(this.shopMultiplier);
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -3095,7 +3089,7 @@ export class BoostBugSpawnModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 1; return 1;
} }
} }
@ -3173,7 +3167,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
const poolType = pokemon.isPlayer() ? ModifierPoolType.PLAYER : pokemon.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; const poolType = pokemon.isPlayer() ? ModifierPoolType.PLAYER : pokemon.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
const transferredModifierTypes: ModifierType[] = []; const transferredModifierTypes: ModifierType[] = [];
const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier const itemModifiers = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === targetPokemon.id && m.isTransferable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; && m.pokemonId === targetPokemon.id && m.isTransferable, targetPokemon.isPlayer()) as 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);
@ -3191,7 +3185,7 @@ 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(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => { heldItemTransferPromises.push(globalScene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => {
if (success) { if (success) {
transferredModifierTypes.push(randItem.type); transferredModifierTypes.push(randItem.type);
itemModifiers.splice(randItemIndex, 1); itemModifiers.splice(randItemIndex, 1);
@ -3201,7 +3195,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
Promise.all(heldItemTransferPromises).then(() => { Promise.all(heldItemTransferPromises).then(() => {
for (const mt of transferredModifierTypes) { for (const mt of transferredModifierTypes) {
pokemon.scene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt)); globalScene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt));
} }
}); });
@ -3320,7 +3314,7 @@ export class IvScannerModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 3; return 3;
} }
} }
@ -3349,7 +3343,7 @@ export class ExtraModifierModifier extends PersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 3; return 3;
} }
} }
@ -3370,17 +3364,16 @@ export class TempExtraModifierModifier extends LapsingPersistentModifier {
* If no existing Silver Pokeballs are found, will add a new one. * If no existing Silver Pokeballs are found, will add a new one.
* @param modifiers {@linkcode PersistentModifier} array of the player's modifiers * @param modifiers {@linkcode PersistentModifier} array of the player's modifiers
* @param _virtual N/A * @param _virtual N/A
* @param scene
* @returns true if the modifier was successfully added or applied, false otherwise * @returns true if the modifier was successfully added or applied, false otherwise
*/ */
add(modifiers: PersistentModifier[], _virtual: boolean, scene: BattleScene): boolean { add(modifiers: PersistentModifier[], _virtual: boolean): boolean {
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (this.match(modifier)) { if (this.match(modifier)) {
const modifierInstance = modifier as TempExtraModifierModifier; const modifierInstance = modifier as TempExtraModifierModifier;
const newBattleCount = this.getMaxBattles() + modifierInstance.getBattleCount(); const newBattleCount = this.getMaxBattles() + modifierInstance.getBattleCount();
modifierInstance.setNewBattleCount(newBattleCount); modifierInstance.setNewBattleCount(newBattleCount);
scene.playSound("se/restore"); globalScene.playSound("se/restore");
return true; return true;
} }
} }
@ -3413,7 +3406,7 @@ export abstract class EnemyPersistentModifier extends PersistentModifier {
super(type, stackCount); super(type, stackCount);
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 5; return 5;
} }
} }
@ -3438,7 +3431,7 @@ abstract class EnemyDamageMultiplierModifier extends EnemyPersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 99; return 99;
} }
} }
@ -3461,7 +3454,7 @@ export class EnemyDamageBoosterModifier extends EnemyDamageMultiplierModifier {
return [ (this.damageMultiplier - 1) * 100 ]; return [ (this.damageMultiplier - 1) * 100 ];
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 999; return 999;
} }
} }
@ -3484,8 +3477,8 @@ export class EnemyDamageReducerModifier extends EnemyDamageMultiplierModifier {
return [ (1 - this.damageMultiplier) * 100 ]; return [ (1 - this.damageMultiplier) * 100 ];
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return scene.currentBattle.waveIndex < 2000 ? super.getMaxStackCount(scene) : 999; return globalScene.currentBattle.waveIndex < 2000 ? super.getMaxStackCount() : 999;
} }
} }
@ -3518,8 +3511,7 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier {
*/ */
override apply(enemyPokemon: Pokemon): boolean { override apply(enemyPokemon: Pokemon): boolean {
if (!enemyPokemon.isFullHp()) { if (!enemyPokemon.isFullHp()) {
const scene = enemyPokemon.scene; globalScene.unshiftPhase(new PokemonHealPhase(enemyPokemon.getBattlerIndex(),
scene.unshiftPhase(new PokemonHealPhase(scene, enemyPokemon.getBattlerIndex(),
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), i18next.t("modifier:enemyTurnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon) }), true, false, false, false, true)); Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), i18next.t("modifier:enemyTurnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon) }), true, false, false, false, true));
return true; return true;
} }
@ -3527,7 +3519,7 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier {
return false; return false;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 10; return 10;
} }
} }
@ -3569,7 +3561,7 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi
return false; return false;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 10; return 10;
} }
} }
@ -3603,7 +3595,7 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier
*/ */
override apply(enemyPokemon: Pokemon): boolean { override apply(enemyPokemon: Pokemon): boolean {
if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < (this.chance * this.getStackCount())) { if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < (this.chance * this.getStackCount())) {
enemyPokemon.scene.queueMessage(getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon))); globalScene.queueMessage(getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)));
enemyPokemon.resetStatus(); enemyPokemon.resetStatus();
enemyPokemon.updateInfo(); enemyPokemon.updateInfo();
return true; return true;
@ -3612,7 +3604,7 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier
return false; return false;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 10; return 10;
} }
} }
@ -3656,7 +3648,7 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 10; return 10;
} }
} }
@ -3697,7 +3689,7 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier {
return true; return true;
} }
getMaxStackCount(scene: BattleScene): number { getMaxStackCount(): number {
return 10; return 10;
} }
} }
@ -3706,18 +3698,17 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier {
* Uses either `MODIFIER_OVERRIDE` in overrides.ts to set {@linkcode PersistentModifier}s for either: * Uses either `MODIFIER_OVERRIDE` in overrides.ts to set {@linkcode PersistentModifier}s for either:
* - The player * - The player
* - The enemy * - The enemy
* @param scene current {@linkcode BattleScene}
* @param isPlayer {@linkcode boolean} for whether the player (`true`) or enemy (`false`) is being overridden * @param isPlayer {@linkcode boolean} for whether the player (`true`) or enemy (`false`) is being overridden
*/ */
export function overrideModifiers(scene: BattleScene, isPlayer: boolean = true): void { export function overrideModifiers(isPlayer: boolean = true): void {
const modifiersOverride: ModifierOverride[] = isPlayer ? Overrides.STARTING_MODIFIER_OVERRIDE : Overrides.OPP_MODIFIER_OVERRIDE; const modifiersOverride: ModifierOverride[] = isPlayer ? Overrides.STARTING_MODIFIER_OVERRIDE : Overrides.OPP_MODIFIER_OVERRIDE;
if (!modifiersOverride || modifiersOverride.length === 0 || !scene) { if (!modifiersOverride || modifiersOverride.length === 0 || !globalScene) {
return; return;
} }
// If it's the opponent, clear all of their current modifiers to avoid stacking // If it's the opponent, clear all of their current modifiers to avoid stacking
if (!isPlayer) { if (!isPlayer) {
scene.clearEnemyModifiers(); globalScene.clearEnemyModifiers();
} }
modifiersOverride.forEach(item => { modifiersOverride.forEach(item => {
@ -3734,9 +3725,9 @@ export function overrideModifiers(scene: BattleScene, isPlayer: boolean = true):
modifier.stackCount = item.count || 1; modifier.stackCount = item.count || 1;
if (isPlayer) { if (isPlayer) {
scene.addModifier(modifier, true, false, false, true); globalScene.addModifier(modifier, true, false, false, true);
} else { } else {
scene.addEnemyModifier(modifier, true, true); globalScene.addEnemyModifier(modifier, true, true);
} }
} }
}); });
@ -3746,18 +3737,17 @@ export function overrideModifiers(scene: BattleScene, isPlayer: boolean = true):
* Uses either `HELD_ITEMS_OVERRIDE` in overrides.ts to set {@linkcode PokemonHeldItemModifier}s for either: * Uses either `HELD_ITEMS_OVERRIDE` in overrides.ts to set {@linkcode PokemonHeldItemModifier}s for either:
* - The first member of the player's team when starting a new game * - The first member of the player's team when starting a new game
* - An enemy {@linkcode Pokemon} being spawned in * - An enemy {@linkcode Pokemon} being spawned in
* @param scene current {@linkcode BattleScene}
* @param pokemon {@linkcode Pokemon} whose held items are being overridden * @param pokemon {@linkcode Pokemon} whose held items are being overridden
* @param isPlayer {@linkcode boolean} for whether the {@linkcode pokemon} is the player's (`true`) or an enemy (`false`) * @param isPlayer {@linkcode boolean} for whether the {@linkcode pokemon} is the player's (`true`) or an enemy (`false`)
*/ */
export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, isPlayer: boolean = true): void { export function overrideHeldItems(pokemon: Pokemon, isPlayer: boolean = true): void {
const heldItemsOverride: ModifierOverride[] = isPlayer ? Overrides.STARTING_HELD_ITEMS_OVERRIDE : Overrides.OPP_HELD_ITEMS_OVERRIDE; const heldItemsOverride: ModifierOverride[] = isPlayer ? Overrides.STARTING_HELD_ITEMS_OVERRIDE : Overrides.OPP_HELD_ITEMS_OVERRIDE;
if (!heldItemsOverride || heldItemsOverride.length === 0 || !scene) { if (!heldItemsOverride || heldItemsOverride.length === 0 || !globalScene) {
return; return;
} }
if (!isPlayer) { if (!isPlayer) {
scene.clearEnemyHeldItemModifiers(pokemon); globalScene.clearEnemyHeldItemModifiers(pokemon);
} }
heldItemsOverride.forEach(item => { heldItemsOverride.forEach(item => {
@ -3775,9 +3765,9 @@ export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, isPlayer
heldItemModifier.pokemonId = pokemon.id; heldItemModifier.pokemonId = pokemon.id;
heldItemModifier.stackCount = qty; heldItemModifier.stackCount = qty;
if (isPlayer) { if (isPlayer) {
scene.addModifier(heldItemModifier, true, false, false, true); globalScene.addModifier(heldItemModifier, true, false, false, true);
} else { } else {
scene.addEnemyModifier(heldItemModifier, true, true); globalScene.addEnemyModifier(heldItemModifier, true, true);
} }
} }
}); });

View File

@ -1,19 +1,19 @@
import { type PokeballCounts } from "#app/battle-scene"; import { type PokeballCounts } from "#app/battle-scene";
import { Gender } from "#app/data/gender"; import type { Gender } from "#app/data/gender";
import { Variant } from "#app/data/variant"; import type { Variant } from "#app/data/variant";
import { type ModifierOverride } from "#app/modifier/modifier-type"; import { type ModifierOverride } from "#app/modifier/modifier-type";
import { Unlockables } from "#app/system/unlockables"; import type { Unlockables } from "#app/system/unlockables";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { EggTier } from "#enums/egg-type"; import type { EggTier } from "#enums/egg-type";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species"; import type { Species } from "#enums/species";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { TimeOfDay } from "#enums/time-of-day"; import type { TimeOfDay } from "#enums/time-of-day";
import { VariantTier } from "#enums/variant-tier"; import type { VariantTier } from "#enums/variant-tier";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
/** /**

View File

@ -1,19 +1,13 @@
import BattleScene from "./battle-scene"; import { globalScene } from "#app/global-scene";
export class Phase { export class Phase {
protected scene: BattleScene;
constructor(scene: BattleScene) {
this.scene = scene;
}
start() { start() {
if (this.scene.abilityBar.shown) { if (globalScene.abilityBar.shown) {
this.scene.abilityBar.resetAutoHideTimer(); globalScene.abilityBar.resetAutoHideTimer();
} }
} }
end() { end() {
this.scene.shiftPhase(); globalScene.shiftPhase();
} }
} }

View File

@ -1,26 +1,26 @@
import BattleScene from "#app/battle-scene";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { regenerateModifierPoolThresholds, ModifierPoolType, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type"; import { regenerateModifierPoolThresholds, ModifierPoolType, getEnemyBuffModifierForWave } from "#app/modifier/modifier-type";
import { EnemyPersistentModifier } from "#app/modifier/modifier"; import { EnemyPersistentModifier } from "#app/modifier/modifier";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import { globalScene } from "#app/global-scene";
export class AddEnemyBuffModifierPhase extends Phase { export class AddEnemyBuffModifierPhase extends Phase {
constructor(scene: BattleScene) { constructor() {
super(scene); super();
} }
start() { start() {
super.start(); super.start();
const waveIndex = this.scene.currentBattle.waveIndex; const waveIndex = globalScene.currentBattle.waveIndex;
const tier = !(waveIndex % 1000) ? ModifierTier.ULTRA : !(waveIndex % 250) ? ModifierTier.GREAT : ModifierTier.COMMON; const tier = !(waveIndex % 1000) ? ModifierTier.ULTRA : !(waveIndex % 250) ? ModifierTier.GREAT : ModifierTier.COMMON;
regenerateModifierPoolThresholds(this.scene.getEnemyParty(), ModifierPoolType.ENEMY_BUFF); regenerateModifierPoolThresholds(globalScene.getEnemyParty(), ModifierPoolType.ENEMY_BUFF);
const count = Math.ceil(waveIndex / 250); const count = Math.ceil(waveIndex / 250);
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
this.scene.addEnemyModifier(getEnemyBuffModifierForWave(tier, this.scene.findModifiers(m => m instanceof EnemyPersistentModifier, false), this.scene), true, true); globalScene.addEnemyModifier(getEnemyBuffModifierForWave(tier, globalScene.findModifiers(m => m instanceof EnemyPersistentModifier, false)), true, true);
} }
this.scene.updateModifiers(false, true).then(() => this.end()); globalScene.updateModifiers(false, true).then(() => this.end());
} }
} }

View File

@ -1,30 +1,31 @@
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import BattleScene from "#app/battle-scene";
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
import { SubstituteTag } from "#app/data/battler-tags"; import { SubstituteTag } from "#app/data/battler-tags";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, getCriticalCaptureChance } from "#app/data/pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, getCriticalCaptureChance } from "#app/data/pokeball";
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims"; import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
import { EnemyPokemon } from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { PokemonPhase } from "#app/phases/pokemon-phase"; import { PokemonPhase } from "#app/phases/pokemon-phase";
import { VictoryPhase } from "#app/phases/victory-phase"; import { VictoryPhase } from "#app/phases/victory-phase";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; import type { PartyOption } from "#app/ui/party-ui-handler";
import { PartyUiMode } from "#app/ui/party-ui-handler";
import { SummaryUiMode } from "#app/ui/summary-ui-handler"; import { SummaryUiMode } from "#app/ui/summary-ui-handler";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import i18next from "i18next"; import i18next from "i18next";
import { globalScene } from "#app/global-scene";
export class AttemptCapturePhase extends PokemonPhase { export class AttemptCapturePhase extends PokemonPhase {
private pokeballType: PokeballType; private pokeballType: PokeballType;
private pokeball: Phaser.GameObjects.Sprite; private pokeball: Phaser.GameObjects.Sprite;
private originalY: number; private originalY: number;
constructor(scene: BattleScene, targetIndex: integer, pokeballType: PokeballType) { constructor(targetIndex: integer, pokeballType: PokeballType) {
super(scene, BattlerIndex.ENEMY + targetIndex); super(BattlerIndex.ENEMY + targetIndex);
this.pokeballType = pokeballType; this.pokeballType = pokeballType;
} }
@ -43,7 +44,7 @@ export class AttemptCapturePhase extends PokemonPhase {
substitute.sprite.setVisible(false); substitute.sprite.setVisible(false);
} }
this.scene.pokeballCounts[this.pokeballType]--; globalScene.pokeballCounts[this.pokeballType]--;
this.originalY = pokemon.y; this.originalY = pokemon.y;
@ -54,21 +55,21 @@ export class AttemptCapturePhase extends PokemonPhase {
const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1; const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier); const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
const shakeProbability = Math.round(65536 / Math.pow((255 / modifiedCatchRate), 0.1875)); // Formula taken from gen 6 const shakeProbability = Math.round(65536 / Math.pow((255 / modifiedCatchRate), 0.1875)); // Formula taken from gen 6
const criticalCaptureChance = getCriticalCaptureChance(this.scene, modifiedCatchRate); const criticalCaptureChance = getCriticalCaptureChance(modifiedCatchRate);
const isCritical = pokemon.randSeedInt(256) < criticalCaptureChance; const isCritical = pokemon.randSeedInt(256) < criticalCaptureChance;
const fpOffset = pokemon.getFieldPositionOffset(); const fpOffset = pokemon.getFieldPositionOffset();
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
this.pokeball = this.scene.addFieldSprite(16, 80, "pb", pokeballAtlasKey); this.pokeball = globalScene.addFieldSprite(16, 80, "pb", pokeballAtlasKey);
this.pokeball.setOrigin(0.5, 0.625); this.pokeball.setOrigin(0.5, 0.625);
this.scene.field.add(this.pokeball); globalScene.field.add(this.pokeball);
this.scene.playSound("se/pb_throw", isCritical ? { rate: 0.2 } : undefined); // Crit catch throws are higher pitched globalScene.playSound("se/pb_throw", isCritical ? { rate: 0.2 } : undefined); // Crit catch throws are higher pitched
this.scene.time.delayedCall(300, () => { globalScene.time.delayedCall(300, () => {
this.scene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon); globalScene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon);
}); });
this.scene.tweens.add({ globalScene.tweens.add({
// Throw animation // Throw animation
targets: this.pokeball, targets: this.pokeball,
x: { value: 236 + fpOffset[0], ease: "Linear" }, x: { value: 236 + fpOffset[0], ease: "Linear" },
@ -77,13 +78,13 @@ export class AttemptCapturePhase extends PokemonPhase {
onComplete: () => { onComplete: () => {
// Ball opens // Ball opens
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); globalScene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
this.scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokemon.tint(getPokeballTintColor(this.pokeballType)); pokemon.tint(getPokeballTintColor(this.pokeballType));
addPokeballOpenParticles(this.scene, this.pokeball.x, this.pokeball.y, this.pokeballType); addPokeballOpenParticles(this.pokeball.x, this.pokeball.y, this.pokeballType);
this.scene.tweens.add({ globalScene.tweens.add({
// Mon enters ball // Mon enters ball
targets: pokemon, targets: pokemon,
duration: 500, duration: 500,
@ -94,14 +95,14 @@ export class AttemptCapturePhase extends PokemonPhase {
// Ball closes // Ball closes
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
pokemon.setVisible(false); pokemon.setVisible(false);
this.scene.playSound("se/pb_catch"); globalScene.playSound("se/pb_catch");
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`)); globalScene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`));
const doShake = () => { const doShake = () => {
// After the overall catch rate check, the game does 3 shake checks before confirming the catch. // After the overall catch rate check, the game does 3 shake checks before confirming the catch.
let shakeCount = 0; let shakeCount = 0;
const pbX = this.pokeball.x; const pbX = this.pokeball.x;
const shakeCounter = this.scene.tweens.addCounter({ const shakeCounter = globalScene.tweens.addCounter({
from: 0, from: 0,
to: 1, to: 1,
repeat: isCritical ? 2 : 4, // Critical captures only perform 1 shake check repeat: isCritical ? 2 : 4, // Critical captures only perform 1 shake check
@ -124,7 +125,7 @@ export class AttemptCapturePhase extends PokemonPhase {
} else if (shakeCount++ < (isCritical ? 1 : 3)) { } else if (shakeCount++ < (isCritical ? 1 : 3)) {
// Shake check (skip check for critical or guaranteed captures, but still play the sound) // Shake check (skip check for critical or guaranteed captures, but still play the sound)
if (pokeballMultiplier === -1 || isCritical || modifiedCatchRate >= 255 || pokemon.randSeedInt(65536) < shakeProbability) { if (pokeballMultiplier === -1 || isCritical || modifiedCatchRate >= 255 || pokemon.randSeedInt(65536) < shakeProbability) {
this.scene.playSound("se/pb_move"); globalScene.playSound("se/pb_move");
} else { } else {
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);
@ -134,21 +135,21 @@ export class AttemptCapturePhase extends PokemonPhase {
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);
} else { } else {
this.scene.playSound("se/pb_lock"); globalScene.playSound("se/pb_lock");
addPokeballCaptureStars(this.scene, this.pokeball); addPokeballCaptureStars(this.pokeball);
const pbTint = this.scene.add.sprite(this.pokeball.x, this.pokeball.y, "pb", "pb"); const pbTint = globalScene.add.sprite(this.pokeball.x, this.pokeball.y, "pb", "pb");
pbTint.setOrigin(this.pokeball.originX, this.pokeball.originY); pbTint.setOrigin(this.pokeball.originX, this.pokeball.originY);
pbTint.setTintFill(0); pbTint.setTintFill(0);
pbTint.setAlpha(0); pbTint.setAlpha(0);
this.scene.field.add(pbTint); globalScene.field.add(pbTint);
this.scene.tweens.add({ globalScene.tweens.add({
targets: pbTint, targets: pbTint,
alpha: 0.375, alpha: 0.375,
duration: 200, duration: 200,
easing: "Sine.easeOut", easing: "Sine.easeOut",
onComplete: () => { onComplete: () => {
this.scene.tweens.add({ globalScene.tweens.add({
targets: pbTint, targets: pbTint,
alpha: 0, alpha: 0,
duration: 200, duration: 200,
@ -166,7 +167,7 @@ export class AttemptCapturePhase extends PokemonPhase {
}; };
// Ball bounces (handled in pokemon.ts) // Ball bounces (handled in pokemon.ts)
this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake, isCritical)); globalScene.time.delayedCall(250, () => doPokeballBounceAnim(this.pokeball, 16, 72, 350, doShake, isCritical));
} }
}); });
} }
@ -176,7 +177,7 @@ export class AttemptCapturePhase extends PokemonPhase {
failCatch(shakeCount: integer) { failCatch(shakeCount: integer) {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
this.scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokemon.setY(this.originalY); pokemon.setY(this.originalY);
if (pokemon.status?.effect !== StatusEffect.SLEEP) { if (pokemon.status?.effect !== StatusEffect.SLEEP) {
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
@ -192,16 +193,16 @@ export class AttemptCapturePhase extends PokemonPhase {
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType); const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`); this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`)); globalScene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
this.scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
scale: 1 scale: 1
}); });
this.scene.currentBattle.lastUsedPokeball = this.pokeballType; globalScene.currentBattle.lastUsedPokeball = this.pokeballType;
this.removePb(); this.removePb();
this.end(); this.end();
} }
@ -212,48 +213,48 @@ export class AttemptCapturePhase extends PokemonPhase {
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) { if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1) {
this.scene.validateAchv(achvs.HIDDEN_ABILITY); globalScene.validateAchv(achvs.HIDDEN_ABILITY);
} }
if (pokemon.species.subLegendary) { if (pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.CATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
} }
if (pokemon.species.legendary) { if (pokemon.species.legendary) {
this.scene.validateAchv(achvs.CATCH_LEGENDARY); globalScene.validateAchv(achvs.CATCH_LEGENDARY);
} }
if (pokemon.species.mythical) { if (pokemon.species.mythical) {
this.scene.validateAchv(achvs.CATCH_MYTHICAL); globalScene.validateAchv(achvs.CATCH_MYTHICAL);
} }
this.scene.pokemonInfoContainer.show(pokemon, true); globalScene.pokemonInfoContainer.show(pokemon, true);
this.scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
this.scene.ui.showText(i18next.t("battle:pokemonCaught", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { globalScene.ui.showText(i18next.t("battle:pokemonCaught", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => {
const end = () => { const end = () => {
this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex));
this.scene.pokemonInfoContainer.hide(); globalScene.pokemonInfoContainer.hide();
this.removePb(); this.removePb();
this.end(); this.end();
}; };
const removePokemon = () => { const removePokemon = () => {
this.scene.addFaintedEnemyScore(pokemon); globalScene.addFaintedEnemyScore(pokemon);
this.scene.getPlayerField().filter(p => p.isActive(true)).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id)); globalScene.getPlayerField().filter(p => p.isActive(true)).forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id));
pokemon.hp = 0; pokemon.hp = 0;
pokemon.trySetStatus(StatusEffect.FAINT); pokemon.trySetStatus(StatusEffect.FAINT);
this.scene.clearEnemyHeldItemModifiers(); globalScene.clearEnemyHeldItemModifiers();
this.scene.field.remove(pokemon, true); globalScene.field.remove(pokemon, true);
}; };
const addToParty = (slotIndex?: number) => { const addToParty = (slotIndex?: number) => {
const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex); const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex);
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); const modifiers = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
if (this.scene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) { if (globalScene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) {
this.scene.validateAchv(achvs.SHINY_PARTY); globalScene.validateAchv(achvs.SHINY_PARTY);
} }
Promise.all(modifiers.map(m => this.scene.addModifier(m, true))).then(() => { Promise.all(modifiers.map(m => globalScene.addModifier(m, true))).then(() => {
this.scene.updateModifiers(true); globalScene.updateModifiers(true);
removePokemon(); removePokemon();
if (newPokemon) { if (newPokemon) {
newPokemon.loadAssets().then(end); newPokemon.loadAssets().then(end);
@ -262,21 +263,21 @@ export class AttemptCapturePhase extends PokemonPhase {
} }
}); });
}; };
Promise.all([ pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon) ]).then(() => { Promise.all([ pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon) ]).then(() => {
if (this.scene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) { if (globalScene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) {
const promptRelease = () => { const promptRelease = () => {
this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { globalScene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); globalScene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
this.scene.ui.setMode(Mode.CONFIRM, () => { globalScene.ui.setMode(Mode.CONFIRM, () => {
const newPokemon = this.scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); const newPokemon = globalScene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon);
this.scene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { globalScene.ui.setMode(Mode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => {
this.scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
promptRelease(); promptRelease();
}); });
}, false); }, false);
}, () => { }, () => {
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { globalScene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => {
this.scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
if (slotIndex < 6) { if (slotIndex < 6) {
addToParty(slotIndex); addToParty(slotIndex);
} else { } else {
@ -285,7 +286,7 @@ export class AttemptCapturePhase extends PokemonPhase {
}); });
}); });
}, () => { }, () => {
this.scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
removePokemon(); removePokemon();
end(); end();
}); });
@ -301,7 +302,7 @@ export class AttemptCapturePhase extends PokemonPhase {
} }
removePb() { removePb() {
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.pokeball, targets: this.pokeball,
duration: 250, duration: 250,
delay: 250, delay: 250,

View File

@ -1,28 +1,29 @@
import BattleScene from "#app/battle-scene";
import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability"; import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability";
import { Stat } from "#app/enums/stat"; import { Stat } from "#app/enums/stat";
import { StatusEffect } from "#app/enums/status-effect"; import { StatusEffect } from "#app/enums/status-effect";
import Pokemon, { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { BattleEndPhase } from "./battle-end-phase"; import { BattleEndPhase } from "./battle-end-phase";
import { NewBattlePhase } from "./new-battle-phase"; import { NewBattlePhase } from "./new-battle-phase";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
import { globalScene } from "#app/global-scene";
export class AttemptRunPhase extends PokemonPhase { export class AttemptRunPhase extends PokemonPhase {
/** For testing purposes: this is to force the pokemon to fail and escape */ /** For testing purposes: this is to force the pokemon to fail and escape */
public forceFailEscape = false; public forceFailEscape = false;
constructor(scene: BattleScene, fieldIndex: number) { constructor(fieldIndex: number) {
super(scene, fieldIndex); super(fieldIndex);
} }
start() { start() {
super.start(); super.start();
const playerField = this.scene.getPlayerField(); const playerField = globalScene.getPlayerField();
const enemyField = this.scene.getEnemyField(); const enemyField = globalScene.getEnemyField();
const playerPokemon = this.getPokemon(); const playerPokemon = this.getPokemon();
@ -33,18 +34,18 @@ export class AttemptRunPhase extends PokemonPhase {
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
if (playerPokemon.randSeedInt(100) < escapeChance.value && !this.forceFailEscape) { if (playerPokemon.randSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
this.scene.playSound("se/flee"); globalScene.playSound("se/flee");
this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); globalScene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
this.scene.tweens.add({ globalScene.tweens.add({
targets: [ this.scene.arenaEnemy, enemyField ].flat(), targets: [ globalScene.arenaEnemy, enemyField ].flat(),
alpha: 0, alpha: 0,
duration: 250, duration: 250,
ease: "Sine.easeIn", ease: "Sine.easeIn",
onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()) onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy())
}); });
this.scene.clearEnemyHeldItemModifiers(); globalScene.clearEnemyHeldItemModifiers();
enemyField.forEach(enemyPokemon => { enemyField.forEach(enemyPokemon => {
enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); enemyPokemon.hideInfo().then(() => enemyPokemon.destroy());
@ -52,11 +53,11 @@ export class AttemptRunPhase extends PokemonPhase {
enemyPokemon.trySetStatus(StatusEffect.FAINT); enemyPokemon.trySetStatus(StatusEffect.FAINT);
}); });
this.scene.pushPhase(new BattleEndPhase(this.scene, false)); globalScene.pushPhase(new BattleEndPhase(false));
this.scene.pushPhase(new NewBattlePhase(this.scene)); globalScene.pushPhase(new NewBattlePhase());
} else { } else {
playerPokemon.turnData.failedRunAway = true; playerPokemon.turnData.failedRunAway = true;
this.scene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); globalScene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500);
} }
this.end(); this.end();
@ -103,6 +104,6 @@ export class AttemptRunPhase extends PokemonPhase {
const escapeSlope = (maxChance - minChance) / speedCap; const escapeSlope = (maxChance - minChance) / speedCap;
// This will calculate the escape chance given all of the above and clamp it to the range of [`minChance`, `maxChance`] // This will calculate the escape chance given all of the above and clamp it to the range of [`minChance`, `maxChance`]
escapeChance.value = Phaser.Math.Clamp(Math.round((escapeSlope * speedRatio) + minChance + (escapeBonus * this.scene.currentBattle.escapeAttempts++)), minChance, maxChance); escapeChance.value = Phaser.Math.Clamp(Math.round((escapeSlope * speedRatio) + minChance + (escapeBonus * globalScene.currentBattle.escapeAttempts++)), minChance, maxChance);
} }
} }

View File

@ -1,4 +1,4 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability"; import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability";
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier"; import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
@ -8,8 +8,8 @@ export class BattleEndPhase extends BattlePhase {
/** If true, will increment battles won */ /** If true, will increment battles won */
isVictory: boolean; isVictory: boolean;
constructor(scene: BattleScene, isVictory: boolean) { constructor(isVictory: boolean) {
super(scene); super();
this.isVictory = isVictory; this.isVictory = isVictory;
} }
@ -17,52 +17,52 @@ export class BattleEndPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
this.scene.gameData.gameStats.battles++; globalScene.gameData.gameStats.battles++;
if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex + 1 > this.scene.gameData.gameStats.highestEndlessWave) { if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex + 1 > globalScene.gameData.gameStats.highestEndlessWave) {
this.scene.gameData.gameStats.highestEndlessWave = this.scene.currentBattle.waveIndex + 1; globalScene.gameData.gameStats.highestEndlessWave = globalScene.currentBattle.waveIndex + 1;
} }
if (this.isVictory) { if (this.isVictory) {
this.scene.currentBattle.addBattleScore(this.scene); globalScene.currentBattle.addBattleScore();
if (this.scene.currentBattle.trainer) { if (globalScene.currentBattle.trainer) {
this.scene.gameData.gameStats.trainersDefeated++; globalScene.gameData.gameStats.trainersDefeated++;
} }
} }
// Endless graceful end // Endless graceful end
if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex >= 5850) { if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex >= 5850) {
this.scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
this.scene.unshiftPhase(new GameOverPhase(this.scene, true)); globalScene.unshiftPhase(new GameOverPhase(true));
} }
for (const pokemon of this.scene.getField()) { for (const pokemon of globalScene.getField()) {
if (pokemon && pokemon.battleSummonData) { if (pokemon && pokemon.battleSummonData) {
pokemon.battleSummonData.waveTurnCount = 1; pokemon.battleSummonData.waveTurnCount = 1;
} }
} }
for (const pokemon of this.scene.getPokemonAllowedInBattle()) { for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon, false, this.isVictory); applyPostBattleAbAttrs(PostBattleAbAttr, pokemon, false, this.isVictory);
} }
if (this.scene.currentBattle.moneyScattered) { if (globalScene.currentBattle.moneyScattered) {
this.scene.currentBattle.pickUpScatteredMoney(this.scene); globalScene.currentBattle.pickUpScatteredMoney();
} }
this.scene.clearEnemyHeldItemModifiers(); globalScene.clearEnemyHeldItemModifiers();
const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; const lapsingModifiers = globalScene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[];
for (const m of lapsingModifiers) { for (const m of lapsingModifiers) {
const args: any[] = []; const args: any[] = [];
if (m instanceof LapsingPokemonHeldItemModifier) { if (m instanceof LapsingPokemonHeldItemModifier) {
args.push(this.scene.getPokemonById(m.pokemonId)); args.push(globalScene.getPokemonById(m.pokemonId));
} }
if (!m.lapse(...args)) { if (!m.lapse(...args)) {
this.scene.removeModifier(m); globalScene.removeModifier(m);
} }
} }
this.scene.updateModifiers().then(() => this.end()); globalScene.updateModifiers().then(() => this.end());
} }
} }

View File

@ -1,15 +1,15 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
export class BattlePhase extends Phase { export class BattlePhase extends Phase {
constructor(scene: BattleScene) { constructor() {
super(scene); super();
} }
showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void {
const sprites = this.scene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? const sprites = globalScene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct?
const tintSprites = this.scene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? const tintSprites = globalScene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct?
for (let i = 0; i < sprites.length; i++) { for (let i = 0; i < sprites.length; i++) {
const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2;
[ sprites[i], tintSprites[i] ].map(sprite => { [ sprites[i], tintSprites[i] ].map(sprite => {
@ -24,8 +24,8 @@ export class BattlePhase extends Phase {
sprites[i].clearTint(); sprites[i].clearTint();
tintSprites[i].clearTint(); tintSprites[i].clearTint();
} }
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.scene.currentBattle.trainer, targets: globalScene.currentBattle.trainer,
x: "-=16", x: "-=16",
y: "+=16", y: "+=16",
alpha: 1, alpha: 1,
@ -35,8 +35,8 @@ export class BattlePhase extends Phase {
} }
hideEnemyTrainer(): void { hideEnemyTrainer(): void {
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.scene.currentBattle.trainer, targets: globalScene.currentBattle.trainer,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
alpha: 0, alpha: 0,

View File

@ -7,6 +7,7 @@ import i18next from "i18next";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { FieldPhase } from "./field-phase"; import { FieldPhase } from "./field-phase";
import { CommonAnimPhase } from "./common-anim-phase"; import { CommonAnimPhase } from "./common-anim-phase";
import { globalScene } from "#app/global-scene";
/** The phase after attacks where the pokemon eat berries */ /** The phase after attacks where the pokemon eat berries */
export class BerryPhase extends FieldPhase { export class BerryPhase extends FieldPhase {
@ -14,7 +15,7 @@ export class BerryPhase extends FieldPhase {
super.start(); super.start();
this.executeForAll((pokemon) => { this.executeForAll((pokemon) => {
const hasUsableBerry = !!this.scene.findModifier((m) => { const hasUsableBerry = !!globalScene.findModifier((m) => {
return m instanceof BerryModifier && m.shouldApply(pokemon); return m instanceof BerryModifier && m.shouldApply(pokemon);
}, pokemon.isPlayer()); }, pokemon.isPlayer());
@ -23,21 +24,21 @@ export class BerryPhase extends FieldPhase {
pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
if (cancelled.value) { if (cancelled.value) {
pokemon.scene.queueMessage(i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
} else { } else {
this.scene.unshiftPhase( globalScene.unshiftPhase(
new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM) new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM)
); );
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) { for (const berryModifier of globalScene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) {
if (berryModifier.consumed) { if (berryModifier.consumed) {
berryModifier.consumed = false; berryModifier.consumed = false;
pokemon.loseHeldItem(berryModifier); pokemon.loseHeldItem(berryModifier);
} }
this.scene.eventTarget.dispatchEvent(new BerryUsedEvent(berryModifier)); // Announce a berry was used globalScene.eventTarget.dispatchEvent(new BerryUsedEvent(berryModifier)); // Announce a berry was used
} }
this.scene.updateModifiers(pokemon.isPlayer()); globalScene.updateModifiers(pokemon.isPlayer());
applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false)); applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false));
} }

View File

@ -1,21 +1,20 @@
import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
export class CheckStatusEffectPhase extends Phase { export class CheckStatusEffectPhase extends Phase {
private order : BattlerIndex[]; private order : BattlerIndex[];
constructor(scene : BattleScene, order : BattlerIndex[]) { constructor(order : BattlerIndex[]) {
super(scene); super();
this.scene = scene;
this.order = order; this.order = order;
} }
start() { start() {
const field = this.scene.getField(); const field = globalScene.getField();
for (const o of this.order) { for (const o of this.order) {
if (field[o].status && field[o].status.isPostTurn()) { if (field[o].status && field[o].status.isPostTurn()) {
this.scene.unshiftPhase(new PostTurnStatusEffectPhase(this.scene, o)); globalScene.unshiftPhase(new PostTurnStatusEffectPhase(o));
} }
} }
this.end(); this.end();

View File

@ -1,4 +1,4 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { BattleStyle } from "#app/enums/battle-style"; import { BattleStyle } from "#app/enums/battle-style";
import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
@ -13,8 +13,8 @@ export class CheckSwitchPhase extends BattlePhase {
protected fieldIndex: integer; protected fieldIndex: integer;
protected useName: boolean; protected useName: boolean;
constructor(scene: BattleScene, fieldIndex: integer, useName: boolean) { constructor(fieldIndex: integer, useName: boolean) {
super(scene); super();
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
this.useName = useName; this.useName = useName;
@ -23,40 +23,40 @@ export class CheckSwitchPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
const pokemon = this.scene.getPlayerField()[this.fieldIndex]; const pokemon = globalScene.getPlayerField()[this.fieldIndex];
// End this phase early... // End this phase early...
// ...if the user is playing in Set Mode // ...if the user is playing in Set Mode
if (this.scene.battleStyle === BattleStyle.SET) { if (globalScene.battleStyle === BattleStyle.SET) {
return super.end(); return super.end();
} }
// ...if the checked Pokemon is somehow not on the field // ...if the checked Pokemon is somehow not on the field
if (this.scene.field.getAll().indexOf(pokemon) === -1) { if (globalScene.field.getAll().indexOf(pokemon) === -1) {
this.scene.unshiftPhase(new SummonMissingPhase(this.scene, this.fieldIndex)); globalScene.unshiftPhase(new SummonMissingPhase(this.fieldIndex));
return super.end(); return super.end();
} }
// ...if there are no other allowed Pokemon in the player's party to switch with // ...if there are no other allowed Pokemon in the player's party to switch with
if (!this.scene.getPlayerParty().slice(1).filter(p => p.isActive()).length) { if (!globalScene.getPlayerParty().slice(1).filter(p => p.isActive()).length) {
return super.end(); return super.end();
} }
// ...or if any player Pokemon has an effect that prevents the checked Pokemon from switching // ...or if any player Pokemon has an effect that prevents the checked Pokemon from switching
if (pokemon.getTag(BattlerTagType.FRENZY) if (pokemon.getTag(BattlerTagType.FRENZY)
|| pokemon.isTrapped() || pokemon.isTrapped()
|| this.scene.getPlayerField().some(p => p.getTag(BattlerTagType.COMMANDED))) { || globalScene.getPlayerField().some(p => p.getTag(BattlerTagType.COMMANDED))) {
return super.end(); return super.end();
} }
this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => { globalScene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => {
this.scene.ui.setMode(Mode.CONFIRM, () => { globalScene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true)); globalScene.unshiftPhase(new SwitchPhase(SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true));
this.end(); this.end();
}, () => { }, () => {
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.end(); this.end();
}); });
}); });

View File

@ -1,14 +1,18 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { TurnCommand, BattleType } from "#app/battle"; import type { TurnCommand } from "#app/battle";
import { TrappedTag, EncoreTag } from "#app/data/battler-tags"; import { BattleType } from "#app/battle";
import { MoveTargetSet, getMoveTargets } from "#app/data/move"; import type { EncoreTag } from "#app/data/battler-tags";
import { TrappedTag } from "#app/data/battler-tags";
import type { MoveTargetSet } from "#app/data/move";
import { getMoveTargets } from "#app/data/move";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { Abilities } from "#app/enums/abilities"; import { Abilities } from "#app/enums/abilities";
import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type";
import { Biome } from "#app/enums/biome"; import { Biome } from "#app/enums/biome";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { FieldPosition } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { Command } from "#app/ui/command-ui-handler"; import { Command } from "#app/ui/command-ui-handler";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
@ -23,8 +27,8 @@ import { ArenaTagType } from "#app/enums/arena-tag-type";
export class CommandPhase extends FieldPhase { export class CommandPhase extends FieldPhase {
protected fieldIndex: integer; protected fieldIndex: integer;
constructor(scene: BattleScene, fieldIndex: integer) { constructor(fieldIndex: integer) {
super(scene); super();
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
} }
@ -32,17 +36,17 @@ export class CommandPhase extends FieldPhase {
start() { start() {
super.start(); super.start();
this.scene.updateGameInfo(); globalScene.updateGameInfo();
const commandUiHandler = this.scene.ui.handlers[Mode.COMMAND]; const commandUiHandler = globalScene.ui.handlers[Mode.COMMAND];
// If one of these conditions is true, we always reset the cursor to Command.FIGHT // If one of these conditions is true, we always reset the cursor to Command.FIGHT
const cursorResetEvent = this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER || const cursorResetEvent = globalScene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER ||
this.scene.currentBattle.battleType === BattleType.TRAINER || globalScene.currentBattle.battleType === BattleType.TRAINER ||
this.scene.arena.biomeType === Biome.END; globalScene.arena.biomeType === Biome.END;
if (commandUiHandler) { if (commandUiHandler) {
if ((this.scene.currentBattle.turn === 1 && (!this.scene.commandCursorMemory || cursorResetEvent)) || commandUiHandler.getCursor() === Command.POKEMON) { if ((globalScene.currentBattle.turn === 1 && (!globalScene.commandCursorMemory || cursorResetEvent)) || commandUiHandler.getCursor() === Command.POKEMON) {
commandUiHandler.setCursor(Command.FIGHT); commandUiHandler.setCursor(Command.FIGHT);
} else { } else {
commandUiHandler.setCursor(commandUiHandler.getCursor()); commandUiHandler.setCursor(commandUiHandler.getCursor());
@ -52,19 +56,19 @@ export class CommandPhase extends FieldPhase {
if (this.fieldIndex) { if (this.fieldIndex) {
// If we somehow are attempting to check the right pokemon but there's only one pokemon out // If we somehow are attempting to check the right pokemon but there's only one pokemon out
// Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching // Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching
if (this.scene.getPlayerField().filter(p => p.isActive()).length === 1) { if (globalScene.getPlayerField().filter(p => p.isActive()).length === 1) {
this.fieldIndex = FieldPosition.CENTER; this.fieldIndex = FieldPosition.CENTER;
} else { } else {
const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1]; const allyCommand = globalScene.currentBattle.turnCommands[this.fieldIndex - 1];
if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) { if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) {
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true }; globalScene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true };
} }
} }
} }
// If the Pokemon has applied Commander's effects to its ally, skip this command // If the Pokemon has applied Commander's effects to its ally, skip this command
if (this.scene.currentBattle?.double && this.getPokemon().getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon(this.scene) === this.getPokemon()) { if (globalScene.currentBattle?.double && this.getPokemon().getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === this.getPokemon()) {
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.FIGHT, move: { move: Moves.NONE, targets: []}, skip: true }; globalScene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.FIGHT, move: { move: Moves.NONE, targets: []}, skip: true };
} }
// Checks if the Pokemon is under the effects of Encore. If so, Encore can end early if the encored move has no more PP. // Checks if the Pokemon is under the effects of Encore. If so, Encore can end early if the encored move has no more PP.
@ -73,11 +77,11 @@ export class CommandPhase extends FieldPhase {
this.getPokemon().lapseTag(BattlerTagType.ENCORE); this.getPokemon().lapseTag(BattlerTagType.ENCORE);
} }
if (this.scene.currentBattle.turnCommands[this.fieldIndex]?.skip) { if (globalScene.currentBattle.turnCommands[this.fieldIndex]?.skip) {
return this.end(); return this.end();
} }
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; const playerPokemon = globalScene.getPlayerField()[this.fieldIndex];
const moveQueue = playerPokemon.getMoveQueue(); const moveQueue = playerPokemon.getMoveQueue();
@ -96,21 +100,21 @@ export class CommandPhase extends FieldPhase {
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct? if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct?
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 }); this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
} else { } else {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
} }
} }
} else { } else {
if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter?.skipToFightInput) { if (globalScene.currentBattle.isBattleMysteryEncounter() && globalScene.currentBattle.mysteryEncounter?.skipToFightInput) {
this.scene.ui.clearText(); globalScene.ui.clearText();
this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); globalScene.ui.setMode(Mode.FIGHT, this.fieldIndex);
} else { } else {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
} }
} }
} }
handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { handleCommand(command: Command, cursor: integer, ...args: any[]): boolean {
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; const playerPokemon = globalScene.getPlayerField()[this.fieldIndex];
let success: boolean = false; let success: boolean = false;
switch (command) { switch (command) {
@ -127,20 +131,20 @@ export class CommandPhase extends FieldPhase {
} }
console.log(moveTargets, getPokemonNameWithAffix(playerPokemon)); console.log(moveTargets, getPokemonNameWithAffix(playerPokemon));
if (moveTargets.targets.length > 1 && moveTargets.multiple) { if (moveTargets.targets.length > 1 && moveTargets.multiple) {
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
} }
if (moveTargets.targets.length <= 1 || moveTargets.multiple) { if (moveTargets.targets.length <= 1 || moveTargets.multiple) {
turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here?
} else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) {
turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here?
} else { } else {
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
} }
this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
success = true; success = true;
} else if (cursor < playerPokemon.getMoveset().length) { } else if (cursor < playerPokemon.getMoveset().length) {
const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct?
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
// Decides between a Disabled, Not Implemented, or No PP translation message // Decides between a Disabled, Not Implemented, or No PP translation message
const errorMessage = const errorMessage =
@ -149,58 +153,58 @@ export class CommandPhase extends FieldPhase {
: move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; : move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP";
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { globalScene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => {
this.scene.ui.clearText(); globalScene.ui.clearText();
this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); globalScene.ui.setMode(Mode.FIGHT, this.fieldIndex);
}, null, true); }, null, true);
} }
break; break;
case Command.BALL: case Command.BALL:
const notInDex = (this.scene.getEnemyField().filter(p => p.isActive(true)).some(p => !p.scene.gameData.dexData[p.species.speciesId].caughtAttr) && this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarterCosts).length - 1); const notInDex = (globalScene.getEnemyField().filter(p => p.isActive(true)).some(p => !globalScene.gameData.dexData[p.species.speciesId].caughtAttr) && globalScene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarterCosts).length - 1);
if (this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || this.scene.gameMode.isFreshStartChallenge() || notInDex )) { if (globalScene.arena.biomeType === Biome.END && (!globalScene.gameMode.isClassic || globalScene.gameMode.isFreshStartChallenge() || notInDex )) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { globalScene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { } else if (globalScene.currentBattle.battleType === BattleType.TRAINER) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => { globalScene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else if (this.scene.currentBattle.isBattleMysteryEncounter() && !this.scene.currentBattle.mysteryEncounter!.catchAllowed) { } else if (globalScene.currentBattle.isBattleMysteryEncounter() && !globalScene.currentBattle.mysteryEncounter!.catchAllowed) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballMysteryEncounter"), null, () => { globalScene.ui.showText(i18next.t("battle:noPokeballMysteryEncounter"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else { } else {
const targets = this.scene.getEnemyField().filter(p => p.isActive(true)).map(p => p.getBattlerIndex()); const targets = globalScene.getEnemyField().filter(p => p.isActive(true)).map(p => p.getBattlerIndex());
if (targets.length > 1) { if (targets.length > 1) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => { globalScene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else if (cursor < 5) { } else if (cursor < 5) {
const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); const targetPokemon = globalScene.getEnemyField().find(p => p.isActive(true));
if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => { globalScene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else { } else {
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; globalScene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; globalScene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets;
if (this.fieldIndex) { if (this.fieldIndex) {
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; globalScene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
} }
success = true; success = true;
} }
@ -210,21 +214,21 @@ export class CommandPhase extends FieldPhase {
case Command.POKEMON: case Command.POKEMON:
case Command.RUN: case Command.RUN:
const isSwitch = command === Command.POKEMON; const isSwitch = command === Command.POKEMON;
const { currentBattle, arena } = this.scene; const { currentBattle, arena } = globalScene;
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed; const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) { if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { globalScene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) { } else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { globalScene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
}, null, true); }, null, true);
} else { } else {
const batonPass = isSwitch && args[0] as boolean; const batonPass = isSwitch && args[0] as boolean;
@ -239,38 +243,38 @@ export class CommandPhase extends FieldPhase {
} }
} else if (trappedAbMessages.length > 0) { } else if (trappedAbMessages.length > 0) {
if (!isSwitch) { if (!isSwitch) {
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
} }
this.scene.ui.showText(trappedAbMessages[0], null, () => { globalScene.ui.showText(trappedAbMessages[0], null, () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
if (!isSwitch) { if (!isSwitch) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
} }
}, null, true); }, null, true);
} else { } else {
const trapTag = playerPokemon.getTag(TrappedTag); const trapTag = playerPokemon.getTag(TrappedTag);
const fairyLockTag = playerPokemon.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.PLAYER); const fairyLockTag = globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.PLAYER);
if (!trapTag && !fairyLockTag) { if (!trapTag && !fairyLockTag) {
i18next.t(`battle:noEscape${isSwitch ? "Switch" : "Flee"}`); i18next.t(`battle:noEscape${isSwitch ? "Switch" : "Flee"}`);
break; break;
} }
if (!isSwitch) { if (!isSwitch) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
} }
const showNoEscapeText = (tag: any) => { const showNoEscapeText = (tag: any) => {
this.scene.ui.showText( globalScene.ui.showText(
i18next.t("battle:noEscapePokemon", { i18next.t("battle:noEscapePokemon", {
pokemonName: tag.sourceId && this.scene.getPokemonById(tag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(tag.sourceId)!) : "", pokemonName: tag.sourceId && globalScene.getPokemonById(tag.sourceId) ? getPokemonNameWithAffix(globalScene.getPokemonById(tag.sourceId)!) : "",
moveName: tag.getMoveName(), moveName: tag.getMoveName(),
escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee") escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee")
}), }),
null, null,
() => { () => {
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
if (!isSwitch) { if (!isSwitch) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
} }
}, },
null, null,
@ -297,8 +301,8 @@ export class CommandPhase extends FieldPhase {
cancel() { cancel() {
if (this.fieldIndex) { if (this.fieldIndex) {
this.scene.unshiftPhase(new CommandPhase(this.scene, 0)); globalScene.unshiftPhase(new CommandPhase(0));
this.scene.unshiftPhase(new CommandPhase(this.scene, 1)); globalScene.unshiftPhase(new CommandPhase(1));
this.end(); this.end();
} }
} }
@ -308,10 +312,10 @@ export class CommandPhase extends FieldPhase {
} }
getPokemon(): PlayerPokemon { getPokemon(): PlayerPokemon {
return this.scene.getPlayerField()[this.fieldIndex]; return globalScene.getPlayerField()[this.fieldIndex];
} }
end() { end() {
this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); globalScene.ui.setMode(Mode.MESSAGE).then(() => super.end());
} }
} }

View File

@ -1,6 +1,7 @@
import BattleScene from "#app/battle-scene"; import type { BattlerIndex } from "#app/battle";
import { BattlerIndex } from "#app/battle"; import { globalScene } from "#app/global-scene";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import type { CommonAnim } from "#app/data/battle-anims";
import { CommonBattleAnim } from "#app/data/battle-anims";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
export class CommonAnimPhase extends PokemonPhase { export class CommonAnimPhase extends PokemonPhase {
@ -8,8 +9,8 @@ export class CommonAnimPhase extends PokemonPhase {
private targetIndex: integer | undefined; private targetIndex: integer | undefined;
private playOnEmptyField: boolean; private playOnEmptyField: boolean;
constructor(scene: BattleScene, battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex | undefined, anim?: CommonAnim, playOnEmptyField: boolean = false) { constructor(battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex, anim?: CommonAnim, playOnEmptyField: boolean = false) {
super(scene, battlerIndex); super(battlerIndex);
this.anim = anim!; // TODO: is this bang correct? this.anim = anim!; // TODO: is this bang correct?
this.targetIndex = targetIndex; this.targetIndex = targetIndex;
@ -21,8 +22,8 @@ export class CommonAnimPhase extends PokemonPhase {
} }
start() { start() {
const target = this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon(); const target = this.targetIndex !== undefined ? (this.player ? globalScene.getEnemyField() : globalScene.getPlayerField())[this.targetIndex] : this.getPokemon();
new CommonBattleAnim(this.anim, this.getPokemon(), target).play(this.scene, false, () => { new CommonBattleAnim(this.anim, this.getPokemon(), target).play(false, () => {
this.end(); this.end();
}); });
} }

View File

@ -1,4 +1,4 @@
import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { type BattlerIndex } from "#app/battle"; import { type BattlerIndex } from "#app/battle";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { type DamageResult, HitResult } from "#app/field/pokemon"; import { type DamageResult, HitResult } from "#app/field/pokemon";
@ -10,8 +10,8 @@ export class DamageAnimPhase extends PokemonPhase {
private damageResult: DamageResult; private damageResult: DamageResult;
private critical: boolean; private critical: boolean;
constructor(scene: BattleScene, battlerIndex: BattlerIndex, amount: integer, damageResult?: DamageResult, critical: boolean = false) { constructor(battlerIndex: BattlerIndex, amount: integer, damageResult?: DamageResult, critical: boolean = false) {
super(scene, battlerIndex); super(battlerIndex);
this.amount = amount; this.amount = amount;
this.damageResult = damageResult || HitResult.EFFECTIVE; this.damageResult = damageResult || HitResult.EFFECTIVE;
@ -22,11 +22,11 @@ export class DamageAnimPhase extends PokemonPhase {
super.start(); super.start();
if (this.damageResult === HitResult.ONE_HIT_KO) { if (this.damageResult === HitResult.ONE_HIT_KO) {
if (this.scene.moveAnimations) { if (globalScene.moveAnimations) {
this.scene.toggleInvert(true); globalScene.toggleInvert(true);
} }
this.scene.time.delayedCall(fixedInt(1000), () => { globalScene.time.delayedCall(fixedInt(1000), () => {
this.scene.toggleInvert(false); globalScene.toggleInvert(false);
this.applyDamage(); this.applyDamage();
}); });
return; return;
@ -42,23 +42,23 @@ export class DamageAnimPhase extends PokemonPhase {
applyDamage() { applyDamage() {
switch (this.damageResult) { switch (this.damageResult) {
case HitResult.EFFECTIVE: case HitResult.EFFECTIVE:
this.scene.playSound("se/hit"); globalScene.playSound("se/hit");
break; break;
case HitResult.SUPER_EFFECTIVE: case HitResult.SUPER_EFFECTIVE:
case HitResult.ONE_HIT_KO: case HitResult.ONE_HIT_KO:
this.scene.playSound("se/hit_strong"); globalScene.playSound("se/hit_strong");
break; break;
case HitResult.NOT_VERY_EFFECTIVE: case HitResult.NOT_VERY_EFFECTIVE:
this.scene.playSound("se/hit_weak"); globalScene.playSound("se/hit_weak");
break; break;
} }
if (this.amount) { if (this.amount) {
this.scene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical); globalScene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical);
} }
if (this.damageResult !== HitResult.OTHER && this.amount > 0) { if (this.damageResult !== HitResult.OTHER && this.amount > 0) {
const flashTimer = this.scene.time.addEvent({ const flashTimer = globalScene.time.addEvent({
delay: 100, delay: 100,
repeat: 5, repeat: 5,
startAt: 200, startAt: 200,
@ -75,8 +75,8 @@ export class DamageAnimPhase extends PokemonPhase {
} }
override end() { override end() {
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
this.scene.initFinalBossPhaseTwo(this.getPokemon()); globalScene.initFinalBossPhaseTwo(this.getPokemon());
} else { } else {
super.end(); super.end();
} }

View File

@ -1,19 +1,20 @@
import BattleScene, { AnySound } from "#app/battle-scene"; import type { AnySound } from "#app/battle-scene";
import { Egg } from "#app/data/egg"; import { globalScene } from "#app/global-scene";
import type { Egg } from "#app/data/egg";
import { EggCountChangedEvent } from "#app/events/egg"; import { EggCountChangedEvent } from "#app/events/egg";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import EggCounterContainer from "#app/ui/egg-counter-container"; import EggCounterContainer from "#app/ui/egg-counter-container";
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; import type EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
import PokemonInfoContainer from "#app/ui/pokemon-info-container"; import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import i18next from "i18next"; import i18next from "i18next";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { EggLapsePhase } from "./egg-lapse-phase"; import type { EggLapsePhase } from "./egg-lapse-phase";
import { EggHatchData } from "#app/data/egg-hatch-data"; import type { EggHatchData } from "#app/data/egg-hatch-data";
import { doShinySparkleAnim } from "#app/field/anims"; import { doShinySparkleAnim } from "#app/field/anims";
@ -67,8 +68,8 @@ export class EggHatchPhase extends Phase {
private evolutionBgm: AnySound; private evolutionBgm: AnySound;
private eggLapsePhase: EggLapsePhase; private eggLapsePhase: EggLapsePhase;
constructor(scene: BattleScene, hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) { constructor(hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) {
super(scene); super();
this.eggLapsePhase = hatchScene; this.eggLapsePhase = hatchScene;
this.egg = egg; this.egg = egg;
this.eggsToHatchCount = eggsToHatchCount; this.eggsToHatchCount = eggsToHatchCount;
@ -77,37 +78,37 @@ export class EggHatchPhase extends Phase {
start() { start() {
super.start(); super.start();
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SCENE).then(() => { globalScene.ui.setModeForceTransition(Mode.EGG_HATCH_SCENE).then(() => {
if (!this.egg) { if (!this.egg) {
return this.end(); return this.end();
} }
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === this.egg.id); const eggIndex = globalScene.gameData.eggs.findIndex(e => e.id === this.egg.id);
if (eggIndex === -1) { if (eggIndex === -1) {
return this.end(); return this.end();
} }
this.scene.gameData.eggs.splice(eggIndex, 1); globalScene.gameData.eggs.splice(eggIndex, 1);
this.scene.fadeOutBgm(undefined, false); globalScene.fadeOutBgm(undefined, false);
this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; this.eggHatchHandler = globalScene.ui.getHandler() as EggHatchSceneHandler;
this.eggHatchContainer = this.eggHatchHandler.eggHatchContainer; this.eggHatchContainer = this.eggHatchHandler.eggHatchContainer;
this.eggHatchBg = this.scene.add.image(0, 0, "default_bg"); this.eggHatchBg = globalScene.add.image(0, 0, "default_bg");
this.eggHatchBg.setOrigin(0, 0); this.eggHatchBg.setOrigin(0, 0);
this.eggHatchContainer.add(this.eggHatchBg); this.eggHatchContainer.add(this.eggHatchBg);
this.eggContainer = this.scene.add.container(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2); this.eggContainer = globalScene.add.container(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2);
this.eggSprite = this.scene.add.sprite(0, 0, "egg", `egg_${this.egg.getKey()}`); this.eggSprite = globalScene.add.sprite(0, 0, "egg", `egg_${this.egg.getKey()}`);
this.eggCrackSprite = this.scene.add.sprite(0, 0, "egg_crack", "0"); this.eggCrackSprite = globalScene.add.sprite(0, 0, "egg_crack", "0");
this.eggCrackSprite.setVisible(false); this.eggCrackSprite.setVisible(false);
this.eggLightraysOverlay = this.scene.add.sprite((-this.eggHatchBg.displayWidth / 2) + 4, -this.eggHatchBg.displayHeight / 2, "egg_lightrays", "3"); this.eggLightraysOverlay = globalScene.add.sprite((-this.eggHatchBg.displayWidth / 2) + 4, -this.eggHatchBg.displayHeight / 2, "egg_lightrays", "3");
this.eggLightraysOverlay.setOrigin(0, 0); this.eggLightraysOverlay.setOrigin(0, 0);
this.eggLightraysOverlay.setVisible(false); this.eggLightraysOverlay.setVisible(false);
@ -116,28 +117,28 @@ export class EggHatchPhase extends Phase {
this.eggContainer.add(this.eggLightraysOverlay); this.eggContainer.add(this.eggLightraysOverlay);
this.eggHatchContainer.add(this.eggContainer); this.eggHatchContainer.add(this.eggContainer);
this.eggCounterContainer = new EggCounterContainer(this.scene, this.eggsToHatchCount); this.eggCounterContainer = new EggCounterContainer(this.eggsToHatchCount);
this.eggHatchContainer.add(this.eggCounterContainer); this.eggHatchContainer.add(this.eggCounterContainer);
const getPokemonSprite = () => { const getPokemonSprite = () => {
const ret = this.scene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, "pkmn__sub"); const ret = globalScene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, "pkmn__sub");
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret; return ret;
}; };
this.eggHatchContainer.add((this.pokemonSprite = getPokemonSprite())); this.eggHatchContainer.add((this.pokemonSprite = getPokemonSprite()));
this.pokemonShinySparkle = this.scene.add.sprite(this.pokemonSprite.x, this.pokemonSprite.y, "shiny"); this.pokemonShinySparkle = globalScene.add.sprite(this.pokemonSprite.x, this.pokemonSprite.y, "shiny");
this.pokemonShinySparkle.setVisible(false); this.pokemonShinySparkle.setVisible(false);
this.eggHatchContainer.add(this.pokemonShinySparkle); this.eggHatchContainer.add(this.pokemonShinySparkle);
this.eggHatchOverlay = this.scene.add.rectangle(0, -this.scene.game.canvas.height / 6, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0xFFFFFF); this.eggHatchOverlay = globalScene.add.rectangle(0, -globalScene.game.canvas.height / 6, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0xFFFFFF);
this.eggHatchOverlay.setOrigin(0, 0); this.eggHatchOverlay.setOrigin(0, 0);
this.eggHatchOverlay.setAlpha(0); this.eggHatchOverlay.setAlpha(0);
this.scene.fieldUI.add(this.eggHatchOverlay); globalScene.fieldUI.add(this.eggHatchOverlay);
this.infoContainer = new PokemonInfoContainer(this.scene); this.infoContainer = new PokemonInfoContainer();
this.infoContainer.setup(); this.infoContainer.setup();
this.eggHatchContainer.add(this.infoContainer); this.eggHatchContainer.add(this.infoContainer);
@ -155,13 +156,13 @@ export class EggHatchPhase extends Phase {
pokemon.loadAssets().then(() => { pokemon.loadAssets().then(() => {
this.canSkip = true; this.canSkip = true;
this.scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
if (!this.hatched) { if (!this.hatched) {
this.evolutionBgm = this.scene.playSoundWithoutBgm("evolution"); this.evolutionBgm = globalScene.playSoundWithoutBgm("evolution");
} }
}); });
this.scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
if (this.hatched) { if (this.hatched) {
return; return;
} }
@ -171,25 +172,25 @@ export class EggHatchPhase extends Phase {
if (this.hatched) { if (this.hatched) {
return; return;
} }
this.scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
if (this.hatched) { if (this.hatched) {
return; return;
} }
this.doSpray(2, this.eggSprite.displayHeight / -4); this.doSpray(2, this.eggSprite.displayHeight / -4);
this.eggCrackSprite.setFrame("1"); this.eggCrackSprite.setFrame("1");
this.scene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("2")); globalScene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("2"));
this.doEggShake(4).then(() => { this.doEggShake(4).then(() => {
if (this.hatched) { if (this.hatched) {
return; return;
} }
this.scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
if (this.hatched) { if (this.hatched) {
return; return;
} }
this.scene.playSound("se/egg_crack"); globalScene.playSound("se/egg_crack");
this.doSpray(4); this.doSpray(4);
this.eggCrackSprite.setFrame("3"); this.eggCrackSprite.setFrame("3");
this.scene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("4")); globalScene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("4"));
this.doEggShake(8, 2).then(() => { this.doEggShake(8, 2).then(() => {
if (!this.hatched) { if (!this.hatched) {
this.doHatch(); this.doHatch();
@ -205,10 +206,10 @@ export class EggHatchPhase extends Phase {
} }
end() { end() {
if (this.scene.findPhase((p) => p instanceof EggHatchPhase)) { if (globalScene.findPhase((p) => p instanceof EggHatchPhase)) {
this.eggHatchHandler.clear(); this.eggHatchHandler.clear();
} else { } else {
this.scene.time.delayedCall(250, () => this.scene.setModifiersVisible(true)); globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true));
} }
super.end(); super.end();
} }
@ -228,14 +229,14 @@ export class EggHatchPhase extends Phase {
if (count === undefined) { if (count === undefined) {
count = 0; count = 0;
} }
this.scene.playSound("se/pb_move"); globalScene.playSound("se/pb_move");
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.eggContainer, targets: this.eggContainer,
x: `-=${intensity / (count ? 1 : 2)}`, x: `-=${intensity / (count ? 1 : 2)}`,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 125, duration: 125,
onComplete: () => { onComplete: () => {
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.eggContainer, targets: this.eggContainer,
x: `+=${intensity}`, x: `+=${intensity}`,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
@ -245,7 +246,7 @@ export class EggHatchPhase extends Phase {
if (count! < repeatCount!) { // we know they are defined if (count! < repeatCount!) { // we know they are defined
return this.doEggShake(intensity, repeatCount, count).then(() => resolve()); return this.doEggShake(intensity, repeatCount, count).then(() => resolve());
} }
this.scene.tweens.add({ globalScene.tweens.add({
targets: this.eggContainer, targets: this.eggContainer,
x: `-=${intensity / 2}`, x: `-=${intensity / 2}`,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
@ -286,14 +287,14 @@ export class EggHatchPhase extends Phase {
this.canSkip = false; this.canSkip = false;
this.hatched = true; this.hatched = true;
if (this.evolutionBgm) { if (this.evolutionBgm) {
SoundFade.fadeOut(this.scene, this.evolutionBgm, Utils.fixedInt(100)); SoundFade.fadeOut(globalScene, this.evolutionBgm, Utils.fixedInt(100));
} }
for (let e = 0; e < 5; e++) { for (let e = 0; e < 5; e++) {
this.scene.time.delayedCall(Utils.fixedInt(375 * e), () => this.scene.playSound("se/egg_hatch", { volume: 1 - (e * 0.2) })); globalScene.time.delayedCall(Utils.fixedInt(375 * e), () => globalScene.playSound("se/egg_hatch", { volume: 1 - (e * 0.2) }));
} }
this.eggLightraysOverlay.setVisible(true); this.eggLightraysOverlay.setVisible(true);
this.eggLightraysOverlay.play("egg_lightrays"); this.eggLightraysOverlay.play("egg_lightrays");
this.scene.tweens.add({ globalScene.tweens.add({
duration: Utils.fixedInt(125), duration: Utils.fixedInt(125),
targets: this.eggHatchOverlay, targets: this.eggHatchOverlay,
alpha: 1, alpha: 1,
@ -303,7 +304,7 @@ export class EggHatchPhase extends Phase {
this.canSkip = true; this.canSkip = true;
} }
}); });
this.scene.time.delayedCall(Utils.fixedInt(1500), () => { globalScene.time.delayedCall(Utils.fixedInt(1500), () => {
this.canSkip = false; this.canSkip = false;
if (!this.skipped) { if (!this.skipped) {
this.doReveal(); this.doReveal();
@ -318,16 +319,16 @@ export class EggHatchPhase extends Phase {
// set the previous dex data so info container can show new unlocks in egg summary // set the previous dex data so info container can show new unlocks in egg summary
const isShiny = this.pokemon.isShiny(); const isShiny = this.pokemon.isShiny();
if (this.pokemon.species.subLegendary) { if (this.pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
} }
if (this.pokemon.species.legendary) { if (this.pokemon.species.legendary) {
this.scene.validateAchv(achvs.HATCH_LEGENDARY); globalScene.validateAchv(achvs.HATCH_LEGENDARY);
} }
if (this.pokemon.species.mythical) { if (this.pokemon.species.mythical) {
this.scene.validateAchv(achvs.HATCH_MYTHICAL); globalScene.validateAchv(achvs.HATCH_MYTHICAL);
} }
if (isShiny) { if (isShiny) {
this.scene.validateAchv(achvs.HATCH_SHINY); globalScene.validateAchv(achvs.HATCH_SHINY);
} }
this.eggContainer.setVisible(false); this.eggContainer.setVisible(false);
const spriteKey = this.pokemon.getSpriteKey(true); const spriteKey = this.pokemon.getSpriteKey(true);
@ -341,33 +342,33 @@ export class EggHatchPhase extends Phase {
this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny); this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny);
this.pokemonSprite.setPipelineData("variant", this.pokemon.variant); this.pokemonSprite.setPipelineData("variant", this.pokemon.variant);
this.pokemonSprite.setVisible(true); this.pokemonSprite.setVisible(true);
this.scene.time.delayedCall(Utils.fixedInt(250), () => { globalScene.time.delayedCall(Utils.fixedInt(250), () => {
this.eggsToHatchCount--; this.eggsToHatchCount--;
this.eggHatchHandler.eventTarget.dispatchEvent(new EggCountChangedEvent(this.eggsToHatchCount)); this.eggHatchHandler.eventTarget.dispatchEvent(new EggCountChangedEvent(this.eggsToHatchCount));
this.pokemon.cry(); this.pokemon.cry();
if (isShiny) { if (isShiny) {
this.scene.time.delayedCall(Utils.fixedInt(500), () => { globalScene.time.delayedCall(Utils.fixedInt(500), () => {
doShinySparkleAnim(this.scene, this.pokemonShinySparkle, this.pokemon.variant); doShinySparkleAnim(this.pokemonShinySparkle, this.pokemon.variant);
}); });
} }
this.scene.time.delayedCall(Utils.fixedInt(!this.skipped ? !isShiny ? 1250 : 1750 : !isShiny ? 250 : 750), () => { globalScene.time.delayedCall(Utils.fixedInt(!this.skipped ? !isShiny ? 1250 : 1750 : !isShiny ? 250 : 750), () => {
this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1); this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1);
this.scene.playSoundWithoutBgm("evolution_fanfare"); globalScene.playSoundWithoutBgm("evolution_fanfare");
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => { globalScene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { globalScene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => { globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => {
this.eggHatchData.setEggMoveUnlocked(value); this.eggHatchData.setEggMoveUnlocked(value);
this.scene.ui.showText("", 0); globalScene.ui.showText("", 0);
this.end(); this.end();
}); });
}); });
}, null, true, 3000); }, null, true, 3000);
}); });
}); });
this.scene.tweens.add({ globalScene.tweens.add({
duration: Utils.fixedInt(this.skipped ? 500 : 3000), duration: Utils.fixedInt(this.skipped ? 500 : 3000),
targets: this.eggHatchOverlay, targets: this.eggHatchOverlay,
alpha: 0, alpha: 0,
@ -391,7 +392,7 @@ export class EggHatchPhase extends Phase {
* @param offsetY how much to offset the Y coordinates * @param offsetY how much to offset the Y coordinates
*/ */
doSpray(intensity: integer, offsetY?: number) { doSpray(intensity: integer, offsetY?: number) {
this.scene.tweens.addCounter({ globalScene.tweens.addCounter({
repeat: intensity, repeat: intensity,
duration: Utils.getFrameMs(1), duration: Utils.getFrameMs(1),
onRepeat: () => { onRepeat: () => {
@ -409,7 +410,7 @@ export class EggHatchPhase extends Phase {
const initialX = this.eggHatchBg.displayWidth / 2; const initialX = this.eggHatchBg.displayWidth / 2;
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY; const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
const shardKey = !this.egg.isManaphyEgg() ? this.egg.tier.toString() : "1"; const shardKey = !this.egg.isManaphyEgg() ? this.egg.tier.toString() : "1";
const particle = this.scene.add.image(initialX, initialY, "egg_shard", `${shardKey}_${Math.floor(trigIndex / 2)}`); const particle = globalScene.add.image(initialX, initialY, "egg_shard", `${shardKey}_${Math.floor(trigIndex / 2)}`);
this.eggHatchContainer.add(particle); this.eggHatchContainer.add(particle);
let f = 0; let f = 0;
@ -417,7 +418,7 @@ export class EggHatchPhase extends Phase {
const speed = 3 - Utils.randInt(8); const speed = 3 - Utils.randInt(8);
const amp = 24 + Utils.randInt(32); const amp = 24 + Utils.randInt(32);
const particleTimer = this.scene.tweens.addCounter({ const particleTimer = globalScene.tweens.addCounter({
repeat: -1, repeat: -1,
duration: Utils.getFrameMs(1), duration: Utils.getFrameMs(1),
onRepeat: () => { onRepeat: () => {

View File

@ -1,12 +1,13 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { Egg, EGG_SEED } from "#app/data/egg"; import type { Egg } from "#app/data/egg";
import { EGG_SEED } from "#app/data/egg";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import i18next from "i18next"; import i18next from "i18next";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { EggHatchPhase } from "./egg-hatch-phase"; import { EggHatchPhase } from "./egg-hatch-phase";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { EggSummaryPhase } from "./egg-summary-phase"; import { EggSummaryPhase } from "./egg-summary-phase";
import { EggHatchData } from "#app/data/egg-hatch-data"; import { EggHatchData } from "#app/data/egg-hatch-data";
@ -18,24 +19,24 @@ export class EggLapsePhase extends Phase {
private eggHatchData: EggHatchData[] = []; private eggHatchData: EggHatchData[] = [];
private readonly minEggsToSkip: number = 2; private readonly minEggsToSkip: number = 2;
constructor(scene: BattleScene) { constructor() {
super(scene); super();
} }
start() { start() {
super.start(); super.start();
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => { const eggsToHatch: Egg[] = globalScene.gameData.eggs.filter((egg: Egg) => {
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1; return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
}); });
const eggsToHatchCount: number = eggsToHatch.length; const eggsToHatchCount: number = eggsToHatch.length;
this.eggHatchData = []; this.eggHatchData = [];
if (eggsToHatchCount > 0) { if (eggsToHatchCount > 0) {
if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) { if (eggsToHatchCount >= this.minEggsToSkip && globalScene.eggSkipPreference === 1) {
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => { globalScene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
// show prompt for skip, blocking inputs for 1 second // show prompt for skip, blocking inputs for 1 second
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt", { eggsToHatch: eggsToHatchCount }), 0); globalScene.ui.showText(i18next.t("battle:eggSkipPrompt", { eggsToHatch: eggsToHatchCount }), 0);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { globalScene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.hatchEggsSkipped(eggsToHatch); this.hatchEggsSkipped(eggsToHatch);
this.showSummary(); this.showSummary();
}, () => { }, () => {
@ -45,13 +46,13 @@ export class EggLapsePhase extends Phase {
null, null, null, 1000, true null, null, null, 1000, true
); );
}, 100, true); }, 100, true);
} else if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 2) { } else if (eggsToHatchCount >= this.minEggsToSkip && globalScene.eggSkipPreference === 2) {
this.scene.queueMessage(i18next.t("battle:eggHatching")); globalScene.queueMessage(i18next.t("battle:eggHatching"));
this.hatchEggsSkipped(eggsToHatch); this.hatchEggsSkipped(eggsToHatch);
this.showSummary(); this.showSummary();
} else { } else {
// regular hatches, no summary // regular hatches, no summary
this.scene.queueMessage(i18next.t("battle:eggHatching")); globalScene.queueMessage(i18next.t("battle:eggHatching"));
this.hatchEggsRegular(eggsToHatch); this.hatchEggsRegular(eggsToHatch);
this.end(); this.end();
} }
@ -67,7 +68,7 @@ export class EggLapsePhase extends Phase {
hatchEggsRegular(eggsToHatch: Egg[]) { hatchEggsRegular(eggsToHatch: Egg[]) {
let eggsToHatchCount: number = eggsToHatch.length; let eggsToHatchCount: number = eggsToHatch.length;
for (const egg of eggsToHatch) { for (const egg of eggsToHatch) {
this.scene.unshiftPhase(new EggHatchPhase(this.scene, this, egg, eggsToHatchCount)); globalScene.unshiftPhase(new EggHatchPhase(this, egg, eggsToHatchCount));
eggsToHatchCount--; eggsToHatchCount--;
} }
} }
@ -83,7 +84,7 @@ export class EggLapsePhase extends Phase {
} }
showSummary() { showSummary() {
this.scene.unshiftPhase(new EggSummaryPhase(this.scene, this.eggHatchData)); globalScene.unshiftPhase(new EggSummaryPhase(this.eggHatchData));
this.end(); this.end();
} }
@ -93,11 +94,11 @@ export class EggLapsePhase extends Phase {
* @param egg egg to hatch * @param egg egg to hatch
*/ */
hatchEggSilently(egg: Egg) { hatchEggSilently(egg: Egg) {
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === egg.id); const eggIndex = globalScene.gameData.eggs.findIndex(e => e.id === egg.id);
if (eggIndex === -1) { if (eggIndex === -1) {
return this.end(); return this.end();
} }
this.scene.gameData.eggs.splice(eggIndex, 1); globalScene.gameData.eggs.splice(eggIndex, 1);
const data = this.generatePokemon(egg); const data = this.generatePokemon(egg);
const pokemon = data.pokemon; const pokemon = data.pokemon;
@ -106,16 +107,16 @@ export class EggLapsePhase extends Phase {
} }
if (pokemon.species.subLegendary) { if (pokemon.species.subLegendary) {
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY); globalScene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
} }
if (pokemon.species.legendary) { if (pokemon.species.legendary) {
this.scene.validateAchv(achvs.HATCH_LEGENDARY); globalScene.validateAchv(achvs.HATCH_LEGENDARY);
} }
if (pokemon.species.mythical) { if (pokemon.species.mythical) {
this.scene.validateAchv(achvs.HATCH_MYTHICAL); globalScene.validateAchv(achvs.HATCH_MYTHICAL);
} }
if (pokemon.isShiny()) { if (pokemon.isShiny()) {
this.scene.validateAchv(achvs.HATCH_SHINY); globalScene.validateAchv(achvs.HATCH_SHINY);
} }
} }
@ -128,9 +129,9 @@ export class EggLapsePhase extends Phase {
generatePokemon(egg: Egg): EggHatchData { generatePokemon(egg: Egg): EggHatchData {
let ret: PlayerPokemon; let ret: PlayerPokemon;
let newHatchData: EggHatchData; let newHatchData: EggHatchData;
this.scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
ret = egg.generatePlayerPokemon(this.scene); ret = egg.generatePlayerPokemon();
newHatchData = new EggHatchData(this.scene, ret, egg.eggMoveIndex); newHatchData = new EggHatchData(ret, egg.eggMoveIndex);
newHatchData.setDex(); newHatchData.setDex();
this.eggHatchData.push(newHatchData); this.eggHatchData.push(newHatchData);

View File

@ -1,7 +1,7 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import { EggHatchData } from "#app/data/egg-hatch-data"; import type { EggHatchData } from "#app/data/egg-hatch-data";
/** /**
* Class that represents the egg summary phase * Class that represents the egg summary phase
@ -11,8 +11,8 @@ import { EggHatchData } from "#app/data/egg-hatch-data";
export class EggSummaryPhase extends Phase { export class EggSummaryPhase extends Phase {
private eggHatchData: EggHatchData[]; private eggHatchData: EggHatchData[];
constructor(scene: BattleScene, eggHatchData: EggHatchData[]) { constructor(eggHatchData: EggHatchData[]) {
super(scene); super();
this.eggHatchData = eggHatchData; this.eggHatchData = eggHatchData;
} }
@ -22,8 +22,8 @@ export class EggSummaryPhase extends Phase {
// updates next pokemon once the current update has been completed // updates next pokemon once the current update has been completed
const updateNextPokemon = (i: number) => { const updateNextPokemon = (i: number) => {
if (i >= this.eggHatchData.length) { if (i >= this.eggHatchData.length) {
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => { globalScene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => {
this.scene.fadeOutBgm(undefined, false); globalScene.fadeOutBgm(undefined, false);
}); });
} else { } else {
@ -40,8 +40,8 @@ export class EggSummaryPhase extends Phase {
} }
end() { end() {
this.scene.time.delayedCall(250, () => this.scene.setModifiersVisible(true)); globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true));
this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => { globalScene.ui.setModeForceTransition(Mode.MESSAGE).then(() => {
super.end(); super.end();
}); });
} }

View File

@ -1,5 +1,5 @@
import { BattlerIndex, BattleType } from "#app/battle"; import { BattlerIndex, BattleType } from "#app/battle";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability"; import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims"; import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
@ -10,7 +10,8 @@ import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encou
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import { getRandomWeatherType } from "#app/data/weather"; import { getRandomWeatherType } from "#app/data/weather";
import { EncounterPhaseEvent } from "#app/events/battle-scene"; import { EncounterPhaseEvent } from "#app/events/battle-scene";
import Pokemon, { FieldPosition } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { FieldPosition } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
@ -41,8 +42,8 @@ import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mys
export class EncounterPhase extends BattlePhase { export class EncounterPhase extends BattlePhase {
private loaded: boolean; private loaded: boolean;
constructor(scene: BattleScene, loaded?: boolean) { constructor(loaded?: boolean) {
super(scene); super();
this.loaded = !!loaded; this.loaded = !!loaded;
} }
@ -50,26 +51,26 @@ export class EncounterPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
this.scene.updateGameInfo(); globalScene.updateGameInfo();
this.scene.initSession(); globalScene.initSession();
this.scene.eventTarget.dispatchEvent(new EncounterPhaseEvent()); globalScene.eventTarget.dispatchEvent(new EncounterPhaseEvent());
// Failsafe if players somehow skip floor 200 in classic mode // Failsafe if players somehow skip floor 200 in classic mode
if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex > 200) { if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) {
this.scene.unshiftPhase(new GameOverPhase(this.scene)); globalScene.unshiftPhase(new GameOverPhase());
} }
const loadEnemyAssets: Promise<void>[] = []; const loadEnemyAssets: Promise<void>[] = [];
const battle = this.scene.currentBattle; const battle = globalScene.currentBattle;
// Generate and Init Mystery Encounter // Generate and Init Mystery Encounter
if (battle.isBattleMysteryEncounter() && !battle.mysteryEncounter) { if (battle.isBattleMysteryEncounter() && !battle.mysteryEncounter) {
this.scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const currentSessionEncounterType = battle.mysteryEncounterType; const currentSessionEncounterType = battle.mysteryEncounterType;
battle.mysteryEncounter = this.scene.getMysteryEncounter(currentSessionEncounterType); battle.mysteryEncounter = globalScene.getMysteryEncounter(currentSessionEncounterType);
}, battle.waveIndex * 16); }, battle.waveIndex * 16);
} }
const mysteryEncounter = battle.mysteryEncounter; const mysteryEncounter = battle.mysteryEncounter;
@ -77,21 +78,21 @@ export class EncounterPhase extends BattlePhase {
// If ME has an onInit() function, call it // If ME has an onInit() function, call it
// Usually used for calculating rand data before initializing anything visual // Usually used for calculating rand data before initializing anything visual
// Also prepopulates any dialogue tokens from encounter/option requirements // Also prepopulates any dialogue tokens from encounter/option requirements
this.scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
if (mysteryEncounter.onInit) { if (mysteryEncounter.onInit) {
mysteryEncounter.onInit(this.scene); mysteryEncounter.onInit();
} }
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); mysteryEncounter.populateDialogueTokensFromRequirements();
}, battle.waveIndex); }, battle.waveIndex);
// Add any special encounter animations to load // Add any special encounter animations to load
if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) { if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) {
loadEnemyAssets.push(initEncounterAnims(this.scene, mysteryEncounter.encounterAnimations).then(() => loadEncounterAnimAssets(this.scene, true))); loadEnemyAssets.push(initEncounterAnims(mysteryEncounter.encounterAnimations).then(() => loadEncounterAnimAssets(true)));
} }
// Add intro visuals for mystery encounter // Add intro visuals for mystery encounter
mysteryEncounter.initIntroVisuals(this.scene); mysteryEncounter.initIntroVisuals();
this.scene.field.add(mysteryEncounter.introVisuals!); globalScene.field.add(mysteryEncounter.introVisuals!);
} }
let totalBst = 0; let totalBst = 0;
@ -105,35 +106,35 @@ export class EncounterPhase extends BattlePhase {
if (battle.battleType === BattleType.TRAINER) { if (battle.battleType === BattleType.TRAINER) {
battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here?
} else { } else {
let enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); let enemySpecies = globalScene.randomSpecies(battle.waveIndex, level, true);
// If player has golden bug net, rolls 10% chance to replace non-boss wave wild species from the golden bug net bug pool // If player has golden bug net, rolls 10% chance to replace non-boss wave wild species from the golden bug net bug pool
if (this.scene.findModifier(m => m instanceof BoostBugSpawnModifier) if (globalScene.findModifier(m => m instanceof BoostBugSpawnModifier)
&& !this.scene.gameMode.isBoss(battle.waveIndex) && !globalScene.gameMode.isBoss(battle.waveIndex)
&& this.scene.arena.biomeType !== Biome.END && globalScene.arena.biomeType !== Biome.END
&& randSeedInt(10) === 0) { && randSeedInt(10) === 0) {
enemySpecies = getGoldenBugNetSpecies(level); enemySpecies = getGoldenBugNetSpecies(level);
} }
battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); battle.enemyParty[e] = globalScene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!globalScene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies));
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
battle.enemyParty[e].ivs = new Array(6).fill(31); battle.enemyParty[e].ivs = new Array(6).fill(31);
} }
this.scene.getPlayerParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => { globalScene.getPlayerParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]); applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
}); });
} }
} }
const enemyPokemon = this.scene.getEnemyParty()[e]; const enemyPokemon = globalScene.getEnemyParty()[e];
if (e < (battle.double ? 2 : 1)) { if (e < (battle.double ? 2 : 1)) {
enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]);
enemyPokemon.resetSummonData(); enemyPokemon.resetSummonData();
} }
if (!this.loaded) { if (!this.loaded) {
this.scene.gameData.setPokemonSeen(enemyPokemon, true, battle.battleType === BattleType.TRAINER || battle?.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE); globalScene.gameData.setPokemonSeen(enemyPokemon, true, battle.battleType === BattleType.TRAINER || battle?.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE);
} }
if (enemyPokemon.species.speciesId === Species.ETERNATUS) { if (enemyPokemon.species.speciesId === Species.ETERNATUS) {
if (this.scene.gameMode.isClassic && (battle.battleSpec === BattleSpec.FINAL_BOSS || this.scene.gameMode.isWaveFinal(battle.waveIndex))) { if (globalScene.gameMode.isClassic && (battle.battleSpec === BattleSpec.FINAL_BOSS || globalScene.gameMode.isWaveFinal(battle.waveIndex))) {
if (battle.battleSpec !== BattleSpec.FINAL_BOSS) { if (battle.battleSpec !== BattleSpec.FINAL_BOSS) {
enemyPokemon.formIndex = 1; enemyPokemon.formIndex = 1;
enemyPokemon.updateScale(); enemyPokemon.updateScale();
@ -153,8 +154,8 @@ export class EncounterPhase extends BattlePhase {
return true; return true;
}); });
if (this.scene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) { if (globalScene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) {
this.scene.validateAchv(achvs.SHINY_PARTY); globalScene.validateAchv(achvs.SHINY_PARTY);
} }
if (battle.battleType === BattleType.TRAINER) { if (battle.battleType === BattleType.TRAINER) {
@ -168,11 +169,11 @@ export class EncounterPhase extends BattlePhase {
} }
// Load Mystery Encounter Exclamation bubble and sfx // Load Mystery Encounter Exclamation bubble and sfx
loadEnemyAssets.push(new Promise<void>(resolve => { loadEnemyAssets.push(new Promise<void>(resolve => {
this.scene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav"); globalScene.loadSe("GEN8- Exclaim", "battle_anims", "GEN8- Exclaim.wav");
this.scene.loadImage("encounter_exclaim", "mystery-encounters"); globalScene.loadImage("encounter_exclaim", "mystery-encounters");
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve()); globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
if (!this.scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
this.scene.load.start(); globalScene.load.start();
} }
})); }));
} else { } else {
@ -196,16 +197,16 @@ export class EncounterPhase extends BattlePhase {
} }
if (e < (battle.double ? 2 : 1)) { if (e < (battle.double ? 2 : 1)) {
if (battle.battleType === BattleType.WILD) { if (battle.battleType === BattleType.WILD) {
this.scene.field.add(enemyPokemon); globalScene.field.add(enemyPokemon);
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
const playerPokemon = this.scene.getPlayerPokemon(); const playerPokemon = globalScene.getPlayerPokemon();
if (playerPokemon?.isOnField()) { if (playerPokemon?.isOnField()) {
this.scene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon); globalScene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon);
} }
enemyPokemon.tint(0, 0.5); enemyPokemon.tint(0, 0.5);
} else if (battle.battleType === BattleType.TRAINER) { } else if (battle.battleType === BattleType.TRAINER) {
enemyPokemon.setVisible(false); enemyPokemon.setVisible(false);
this.scene.currentBattle.trainer?.tint(0, 0.5); globalScene.currentBattle.trainer?.tint(0, 0.5);
} }
if (battle.double) { if (battle.double) {
enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT); enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT);
@ -215,62 +216,56 @@ export class EncounterPhase extends BattlePhase {
}); });
if (!this.loaded && battle.battleType !== BattleType.MYSTERY_ENCOUNTER) { if (!this.loaded && battle.battleType !== BattleType.MYSTERY_ENCOUNTER) {
regenerateModifierPoolThresholds(this.scene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD); regenerateModifierPoolThresholds(globalScene.getEnemyField(), battle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD);
this.scene.generateEnemyModifiers(); globalScene.generateEnemyModifiers();
overrideModifiers(this.scene, false); overrideModifiers(false);
this.scene.getEnemyField().forEach(enemy => { globalScene.getEnemyField().forEach(enemy => {
overrideHeldItems(this.scene, enemy, false); overrideHeldItems(enemy, false);
}); });
} }
this.scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
if (!this.loaded) { if (!this.loaded) {
this.trySetWeatherIfNewBiome(); // Set weather before session gets saved this.trySetWeatherIfNewBiome(); // Set weather before session gets saved
// Game syncs to server on waves X1 and X6 (As of 1.2.0) // Game syncs to server on waves X1 and X6 (As of 1.2.0)
this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 5 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => { globalScene.gameData.saveAll(true, battle.waveIndex % 5 === 1 || (globalScene.lastSavePlayTime ?? 0) >= 300).then(success => {
this.scene.disableMenu = false; globalScene.disableMenu = false;
if (!success) { if (!success) {
return this.scene.reset(true); return globalScene.reset(true);
} }
this.doEncounter(); this.doEncounter();
this.scene.resetSeed(); globalScene.resetSeed();
}); });
} else { } else {
this.doEncounter(); this.doEncounter();
this.scene.resetSeed(); globalScene.resetSeed();
} }
}); });
}); });
} }
doEncounter() { doEncounter() {
this.scene.playBgm(undefined, true); globalScene.playBgm(undefined, true);
this.scene.updateModifiers(false); globalScene.updateModifiers(false);
this.scene.setFieldScale(1); globalScene.setFieldScale(1);
/*if (startingWave > 10) { const { battleType, waveIndex } = globalScene.currentBattle;
for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) if (globalScene.isMysteryEncounterValidForWave(battleType, waveIndex) && !globalScene.currentBattle.isBattleMysteryEncounter()) {
this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getPlayerParty())[0].type.newModifier(), true);
this.scene.updateModifiers(true);
}*/
const { battleType, waveIndex } = this.scene.currentBattle;
if (this.scene.isMysteryEncounterValidForWave(battleType, waveIndex) && !this.scene.currentBattle.isBattleMysteryEncounter()) {
// Increment ME spawn chance if an ME could have spawned but did not // Increment ME spawn chance if an ME could have spawned but did not
// Only do this AFTER session has been saved to avoid duplicating increments // Only do this AFTER session has been saved to avoid duplicating increments
this.scene.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS; globalScene.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS;
} }
for (const pokemon of this.scene.getPlayerParty()) { for (const pokemon of globalScene.getPlayerParty()) {
if (pokemon) { if (pokemon) {
pokemon.resetBattleData(); pokemon.resetBattleData();
} }
} }
const enemyField = this.scene.getEnemyField(); const enemyField = globalScene.getEnemyField();
this.scene.tweens.add({ globalScene.tweens.add({
targets: [ this.scene.arenaEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.arenaPlayer, this.scene.trainer ].flat(), targets: [ globalScene.arenaEnemy, globalScene.currentBattle.trainer, enemyField, globalScene.arenaPlayer, globalScene.trainer ].flat(),
x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 2 + (enemyField.length) ? value + 300 : value - 300, x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 2 + (enemyField.length) ? value + 300 : value - 300,
duration: 2000, duration: 2000,
onComplete: () => { onComplete: () => {
@ -280,13 +275,13 @@ export class EncounterPhase extends BattlePhase {
} }
}); });
const encounterIntroVisuals = this.scene.currentBattle?.mysteryEncounter?.introVisuals; const encounterIntroVisuals = globalScene.currentBattle?.mysteryEncounter?.introVisuals;
if (encounterIntroVisuals) { if (encounterIntroVisuals) {
const enterFromRight = encounterIntroVisuals.enterFromRight; const enterFromRight = encounterIntroVisuals.enterFromRight;
if (enterFromRight) { if (enterFromRight) {
encounterIntroVisuals.x += 500; encounterIntroVisuals.x += 500;
} }
this.scene.tweens.add({ globalScene.tweens.add({
targets: encounterIntroVisuals, targets: encounterIntroVisuals,
x: enterFromRight ? "-=200" : "+=300", x: enterFromRight ? "-=200" : "+=300",
duration: 2000 duration: 2000
@ -295,18 +290,18 @@ export class EncounterPhase extends BattlePhase {
} }
getEncounterMessage(): string { getEncounterMessage(): string {
const enemyField = this.scene.getEnemyField(); const enemyField = globalScene.getEnemyField();
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { if (globalScene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
return i18next.t("battle:bossAppeared", { bossName: getPokemonNameWithAffix(enemyField[0]) }); return i18next.t("battle:bossAppeared", { bossName: getPokemonNameWithAffix(enemyField[0]) });
} }
if (this.scene.currentBattle.battleType === BattleType.TRAINER) { if (globalScene.currentBattle.battleType === BattleType.TRAINER) {
if (this.scene.currentBattle.double) { if (globalScene.currentBattle.double) {
return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); return i18next.t("battle:trainerAppearedDouble", { trainerName: globalScene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) });
} else { } else {
return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); return i18next.t("battle:trainerAppeared", { trainerName: globalScene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) });
} }
} }
@ -316,75 +311,75 @@ export class EncounterPhase extends BattlePhase {
} }
doEncounterCommon(showEncounterMessage: boolean = true) { doEncounterCommon(showEncounterMessage: boolean = true) {
const enemyField = this.scene.getEnemyField(); const enemyField = globalScene.getEnemyField();
if (this.scene.currentBattle.battleType === BattleType.WILD) { if (globalScene.currentBattle.battleType === BattleType.WILD) {
enemyField.forEach(enemyPokemon => { enemyField.forEach(enemyPokemon => {
enemyPokemon.untint(100, "Sine.easeOut"); enemyPokemon.untint(100, "Sine.easeOut");
enemyPokemon.cry(); enemyPokemon.cry();
enemyPokemon.showInfo(); enemyPokemon.showInfo();
if (enemyPokemon.isShiny()) { if (enemyPokemon.isShiny()) {
this.scene.validateAchv(achvs.SEE_SHINY); globalScene.validateAchv(achvs.SEE_SHINY);
} }
}); });
this.scene.updateFieldScale(); globalScene.updateFieldScale();
if (showEncounterMessage) { if (showEncounterMessage) {
this.scene.ui.showText(this.getEncounterMessage(), null, () => this.end(), 1500); globalScene.ui.showText(this.getEncounterMessage(), null, () => this.end(), 1500);
} else { } else {
this.end(); this.end();
} }
} else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { } else if (globalScene.currentBattle.battleType === BattleType.TRAINER) {
const trainer = this.scene.currentBattle.trainer; const trainer = globalScene.currentBattle.trainer;
trainer?.untint(100, "Sine.easeOut"); trainer?.untint(100, "Sine.easeOut");
trainer?.playAnim(); trainer?.playAnim();
const doSummon = () => { const doSummon = () => {
this.scene.currentBattle.started = true; globalScene.currentBattle.started = true;
this.scene.playBgm(undefined); globalScene.playBgm(undefined);
this.scene.pbTray.showPbTray(this.scene.getPlayerParty()); globalScene.pbTray.showPbTray(globalScene.getPlayerParty());
this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); globalScene.pbTrayEnemy.showPbTray(globalScene.getEnemyParty());
const doTrainerSummon = () => { const doTrainerSummon = () => {
this.hideEnemyTrainer(); this.hideEnemyTrainer();
const availablePartyMembers = this.scene.getEnemyParty().filter(p => !p.isFainted()).length; const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length;
this.scene.unshiftPhase(new SummonPhase(this.scene, 0, false)); globalScene.unshiftPhase(new SummonPhase(0, false));
if (this.scene.currentBattle.double && availablePartyMembers > 1) { if (globalScene.currentBattle.double && availablePartyMembers > 1) {
this.scene.unshiftPhase(new SummonPhase(this.scene, 1, false)); globalScene.unshiftPhase(new SummonPhase(1, false));
} }
this.end(); this.end();
}; };
if (showEncounterMessage) { if (showEncounterMessage) {
this.scene.ui.showText(this.getEncounterMessage(), null, doTrainerSummon, 1500, true); globalScene.ui.showText(this.getEncounterMessage(), null, doTrainerSummon, 1500, true);
} else { } else {
doTrainerSummon(); doTrainerSummon();
} }
}; };
const encounterMessages = this.scene.currentBattle.trainer?.getEncounterMessages(); const encounterMessages = globalScene.currentBattle.trainer?.getEncounterMessages();
if (!encounterMessages?.length) { if (!encounterMessages?.length) {
doSummon(); doSummon();
} else { } else {
let message: string; let message: string;
this.scene.executeWithSeedOffset(() => message = randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); globalScene.executeWithSeedOffset(() => message = randSeedItem(encounterMessages), globalScene.currentBattle.waveIndex);
message = message!; // tell TS compiler it's defined now message = message!; // tell TS compiler it's defined now
const showDialogueAndSummon = () => { const showDialogueAndSummon = () => {
this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { globalScene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => {
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doSummon()));
}); });
}; };
if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { if (globalScene.currentBattle.trainer?.config.hasCharSprite && !globalScene.ui.shouldSkipDialogue(message)) {
this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? globalScene.showFieldOverlay(500).then(() => globalScene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct?
} else { } else {
showDialogueAndSummon(); showDialogueAndSummon();
} }
} }
} else if (this.scene.currentBattle.isBattleMysteryEncounter() && this.scene.currentBattle.mysteryEncounter) { } else if (globalScene.currentBattle.isBattleMysteryEncounter() && globalScene.currentBattle.mysteryEncounter) {
const encounter = this.scene.currentBattle.mysteryEncounter; const encounter = globalScene.currentBattle.mysteryEncounter;
const introVisuals = encounter.introVisuals; const introVisuals = encounter.introVisuals;
introVisuals?.playAnim(); introVisuals?.playAnim();
if (encounter.onVisualsStart) { if (encounter.onVisualsStart) {
encounter.onVisualsStart(this.scene); encounter.onVisualsStart();
} else if (encounter.spriteConfigs && introVisuals) { } else if (encounter.spriteConfigs && introVisuals) {
// If the encounter doesn't have any special visual intro, show sparkle for shiny Pokemon // If the encounter doesn't have any special visual intro, show sparkle for shiny Pokemon
introVisuals.playShinySparkles(); introVisuals.playShinySparkles();
@ -392,10 +387,10 @@ export class EncounterPhase extends BattlePhase {
const doEncounter = () => { const doEncounter = () => {
const doShowEncounterOptions = () => { const doShowEncounterOptions = () => {
this.scene.ui.clearText(); globalScene.ui.clearText();
this.scene.ui.getMessageHandler().hideNameText(); globalScene.ui.getMessageHandler().hideNameText();
this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); globalScene.unshiftPhase(new MysteryEncounterPhase());
this.end(); this.end();
}; };
@ -409,13 +404,13 @@ export class EncounterPhase extends BattlePhase {
const showNextDialogue = () => { const showNextDialogue = () => {
const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue; const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue;
const dialogue = introDialogue[i]; const dialogue = introDialogue[i];
const title = getEncounterText(this.scene, dialogue?.speaker); const title = getEncounterText(dialogue?.speaker);
const text = getEncounterText(this.scene, dialogue.text)!; const text = getEncounterText(dialogue.text)!;
i++; i++;
if (title) { if (title) {
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0); globalScene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0);
} else { } else {
this.scene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true); globalScene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
} }
}; };
@ -433,110 +428,110 @@ export class EncounterPhase extends BattlePhase {
if (!encounterMessage) { if (!encounterMessage) {
doEncounter(); doEncounter();
} else { } else {
doTrainerExclamation(this.scene); doTrainerExclamation();
this.scene.ui.showDialogue(encounterMessage, "???", null, () => { globalScene.ui.showDialogue(encounterMessage, "???", null, () => {
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doEncounter())); globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doEncounter()));
}); });
} }
} }
} }
end() { end() {
const enemyField = this.scene.getEnemyField(); const enemyField = globalScene.getEnemyField();
enemyField.forEach((enemyPokemon, e) => { enemyField.forEach((enemyPokemon, e) => {
if (enemyPokemon.isShiny()) { if (enemyPokemon.isShiny()) {
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); globalScene.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e));
} }
/** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */ /** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */
if (enemyPokemon.species.speciesId === Species.ETERNATUS && (this.scene.gameMode.isBattleClassicFinalBoss(this.scene.currentBattle.waveIndex) || this.scene.gameMode.isEndlessMajorBoss(this.scene.currentBattle.waveIndex))) { if (enemyPokemon.species.speciesId === Species.ETERNATUS && (globalScene.gameMode.isBattleClassicFinalBoss(globalScene.currentBattle.waveIndex) || globalScene.gameMode.isEndlessMajorBoss(globalScene.currentBattle.waveIndex))) {
const enemyMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier, false) as TurnHeldItemTransferModifier; const enemyMBH = globalScene.findModifier(m => m instanceof TurnHeldItemTransferModifier, false) as TurnHeldItemTransferModifier;
if (enemyMBH) { if (enemyMBH) {
this.scene.removeModifier(enemyMBH, true); globalScene.removeModifier(enemyMBH, true);
enemyMBH.setTransferrableFalse(); enemyMBH.setTransferrableFalse();
this.scene.addEnemyModifier(enemyMBH); globalScene.addEnemyModifier(enemyMBH);
} }
} }
}); });
if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) { if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(globalScene.currentBattle.battleType)) {
enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => { enemyField.map(p => globalScene.pushConditionalPhase(new PostSummonPhase(p.getBattlerIndex()), () => {
// if there is not a player party, we can't continue // if there is not a player party, we can't continue
if (!this.scene.getPlayerParty().length) { if (!globalScene.getPlayerParty().length) {
return false; return false;
} }
// how many player pokemon are on the field ? // how many player pokemon are on the field ?
const pokemonsOnFieldCount = this.scene.getPlayerParty().filter(p => p.isOnField()).length; const pokemonsOnFieldCount = globalScene.getPlayerParty().filter(p => p.isOnField()).length;
// if it's a 2vs1, there will never be a 2nd pokemon on our field even // if it's a 2vs1, there will never be a 2nd pokemon on our field even
const requiredPokemonsOnField = Math.min(this.scene.getPlayerParty().filter((p) => !p.isFainted()).length, 2); const requiredPokemonsOnField = Math.min(globalScene.getPlayerParty().filter((p) => !p.isFainted()).length, 2);
// if it's a double, there should be 2, otherwise 1 // if it's a double, there should be 2, otherwise 1
if (this.scene.currentBattle.double) { if (globalScene.currentBattle.double) {
return pokemonsOnFieldCount === requiredPokemonsOnField; return pokemonsOnFieldCount === requiredPokemonsOnField;
} }
return pokemonsOnFieldCount === 1; return pokemonsOnFieldCount === 1;
})); }));
const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier); const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
if (ivScannerModifier) { if (ivScannerModifier) {
enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)))); enemyField.map(p => globalScene.pushPhase(new ScanIvsPhase(p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6))));
} }
} }
if (!this.loaded) { if (!this.loaded) {
const availablePartyMembers = this.scene.getPokemonAllowedInBattle(); const availablePartyMembers = globalScene.getPokemonAllowedInBattle();
if (!availablePartyMembers[0].isOnField()) { if (!availablePartyMembers[0].isOnField()) {
this.scene.pushPhase(new SummonPhase(this.scene, 0)); globalScene.pushPhase(new SummonPhase(0));
} }
if (this.scene.currentBattle.double) { if (globalScene.currentBattle.double) {
if (availablePartyMembers.length > 1) { if (availablePartyMembers.length > 1) {
this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, true)); globalScene.pushPhase(new ToggleDoublePositionPhase(true));
if (!availablePartyMembers[1].isOnField()) { if (!availablePartyMembers[1].isOnField()) {
this.scene.pushPhase(new SummonPhase(this.scene, 1)); globalScene.pushPhase(new SummonPhase(1));
} }
} }
} else { } else {
if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) {
this.scene.pushPhase(new ReturnPhase(this.scene, 1)); globalScene.pushPhase(new ReturnPhase(1));
} }
this.scene.pushPhase(new ToggleDoublePositionPhase(this.scene, false)); globalScene.pushPhase(new ToggleDoublePositionPhase(false));
} }
if (this.scene.currentBattle.battleType !== BattleType.TRAINER && (this.scene.currentBattle.waveIndex > 1 || !this.scene.gameMode.isDaily)) { if (globalScene.currentBattle.battleType !== BattleType.TRAINER && (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily)) {
const minPartySize = this.scene.currentBattle.double ? 2 : 1; const minPartySize = globalScene.currentBattle.double ? 2 : 1;
if (availablePartyMembers.length > minPartySize) { if (availablePartyMembers.length > minPartySize) {
this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double)); globalScene.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double));
if (this.scene.currentBattle.double) { if (globalScene.currentBattle.double) {
this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double)); globalScene.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double));
} }
} }
} }
} }
handleTutorial(this.scene, Tutorial.Access_Menu).then(() => super.end()); handleTutorial(Tutorial.Access_Menu).then(() => super.end());
} }
tryOverrideForBattleSpec(): boolean { tryOverrideForBattleSpec(): boolean {
switch (this.scene.currentBattle.battleSpec) { switch (globalScene.currentBattle.battleSpec) {
case BattleSpec.FINAL_BOSS: case BattleSpec.FINAL_BOSS:
const enemy = this.scene.getEnemyPokemon(); const enemy = globalScene.getEnemyPokemon();
this.scene.ui.showText(this.getEncounterMessage(), null, () => { globalScene.ui.showText(this.getEncounterMessage(), null, () => {
const localizationKey = "battleSpecDialogue:encounter"; const localizationKey = "battleSpecDialogue:encounter";
if (this.scene.ui.shouldSkipDialogue(localizationKey)) { if (globalScene.ui.shouldSkipDialogue(localizationKey)) {
// Logging mirrors logging found in dialogue-ui-handler // Logging mirrors logging found in dialogue-ui-handler
console.log(`Dialogue ${localizationKey} skipped`); console.log(`Dialogue ${localizationKey} skipped`);
this.doEncounterCommon(false); this.doEncounterCommon(false);
} else { } else {
const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed; const count = 5643853 + globalScene.gameData.gameStats.classicSessionsPlayed;
// The line below checks if an English ordinal is necessary or not based on whether an entry for encounterLocalizationKey exists in the language or not. // The line below checks if an English ordinal is necessary or not based on whether an entry for encounterLocalizationKey exists in the language or not.
const ordinalUsed = !i18next.exists(localizationKey, { fallbackLng: []}) || i18next.resolvedLanguage === "en" ? i18next.t("battleSpecDialogue:key", { count: count, ordinal: true }) : ""; const ordinalUsed = !i18next.exists(localizationKey, { fallbackLng: []}) || i18next.resolvedLanguage === "en" ? i18next.t("battleSpecDialogue:key", { count: count, ordinal: true }) : "";
const cycleCount = count.toLocaleString() + ordinalUsed; const cycleCount = count.toLocaleString() + ordinalUsed;
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET; const genderIndex = globalScene.gameData.gender ?? PlayerGender.UNSET;
const genderStr = PlayerGender[genderIndex].toLowerCase(); const genderStr = PlayerGender[genderIndex].toLowerCase();
const encounterDialogue = i18next.t(localizationKey, { context: genderStr, cycleCount: cycleCount }); const encounterDialogue = i18next.t(localizationKey, { context: genderStr, cycleCount: cycleCount });
if (!this.scene.gameData.getSeenDialogues()[localizationKey]) { if (!globalScene.gameData.getSeenDialogues()[localizationKey]) {
this.scene.gameData.saveSeenDialogue(localizationKey); globalScene.gameData.saveSeenDialogue(localizationKey);
} }
this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => { globalScene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => {
this.doEncounterCommon(false); this.doEncounterCommon(false);
}); });
} }
@ -556,7 +551,7 @@ export class EncounterPhase extends BattlePhase {
*/ */
trySetWeatherIfNewBiome(): void { trySetWeatherIfNewBiome(): void {
if (!this.loaded) { if (!this.loaded) {
this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false); globalScene.arena.trySetWeather(getRandomWeatherType(globalScene.arena), false);
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More