From e10a9caddc4899809bad6df46654d1f2c94da827 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Mon, 15 Jul 2024 21:45:33 -0400 Subject: [PATCH 01/27] commit changes --- public/images/mystery-encounters/mud.json | 90 ++++----- public/images/mystery-encounters/mud.png | Bin 361 -> 375 bytes .../encounters/fiery-fallout-encounter.ts | 173 ++++++++++++++++++ .../encounters/safari-zone-encounter.ts | 63 ++++--- .../mystery-encounters/mystery-encounter.ts | 32 +++- .../mystery-encounters/mystery-encounters.ts | 6 +- .../utils/encounter-phase-utils.ts | 44 ++++- .../utils/encounter-pokemon-utils.ts | 3 +- src/enums/mystery-encounter-type.ts | 3 +- src/locales/en/mystery-encounter.ts | 20 ++ src/overrides.ts | 4 +- src/phases.ts | 38 ++-- src/phases/mystery-encounter-phase.ts | 9 +- 13 files changed, 380 insertions(+), 105 deletions(-) create mode 100644 src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts diff --git a/public/images/mystery-encounters/mud.json b/public/images/mystery-encounters/mud.json index 804eed36052..505a6fadd27 100644 --- a/public/images/mystery-encounters/mud.json +++ b/public/images/mystery-encounters/mud.json @@ -4,51 +4,72 @@ "image": "mud.png", "format": "RGBA8888", "size": { - "w": 18, - "h": 55 + "w": 14, + "h": 68 }, "scale": 1, "frames": [ { - "filename": "0002.png", + "filename": "0001.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 16, - "h": 16 + "w": 12, + "h": 20 }, "spriteSourceSize": { "x": 0, - "y": 3, - "w": 16, + "y": 0, + "w": 12, "h": 13 }, "frame": { "x": 1, "y": 1, - "w": 16, + "w": 12, "h": 13 } }, + { + "filename": "0002.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 12, + "h": 20 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 12, + "h": 14 + }, + "frame": { + "x": 1, + "y": 16, + "w": 12, + "h": 14 + } + }, { "filename": "0003.png", "rotated": false, "trimmed": true, "sourceSize": { - "w": 16, - "h": 16 + "w": 12, + "h": 20 }, "spriteSourceSize": { "x": 0, - "y": 4, - "w": 16, - "h": 12 + "y": 1, + "w": 12, + "h": 16 }, "frame": { "x": 1, - "y": 16, - "w": 16, - "h": 12 + "y": 32, + "w": 12, + "h": 16 } }, { @@ -56,41 +77,20 @@ "rotated": false, "trimmed": true, "sourceSize": { - "w": 16, - "h": 16 + "w": 12, + "h": 20 }, "spriteSourceSize": { "x": 0, - "y": 7, - "w": 16, - "h": 9 - }, - "frame": { - "x": 1, - "y": 30, - "w": 16, - "h": 9 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 16, - "h": 16 - }, - "spriteSourceSize": { - "x": 1, "y": 3, - "w": 14, - "h": 13 + "w": 12, + "h": 17 }, "frame": { "x": 1, - "y": 41, - "w": 14, - "h": 13 + "y": 50, + "w": 12, + "h": 17 } } ] @@ -99,6 +99,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:a9f7ae83758a2dffaacdaba2ee9dc2e2:0ebff9db47ce74a0ec049f5d74d589fa:c64f6b8befc3d5e9f836246d2b9536be$" + "smartupdate": "$TexturePacker:SmartUpdate:4f18a8effb8f01eb70f9f25b8294c1bf:ad663a73c51f780bbf45d00a52519553:c64f6b8befc3d5e9f836246d2b9536be$" } } diff --git a/public/images/mystery-encounters/mud.png b/public/images/mystery-encounters/mud.png index 89f174bef74e73d32fd9538b3be7e58339212226..2ba7cb00047aa4b3207ec70993c6fef3e34e7834 100644 GIT binary patch delta 349 zcmV-j0iyot0`~$%iBL{Q4GJ0x0000DNk~Le0000E0000)2m=5B0ESic=>Px#7*I@9 zMNDaNp>tP}UqEw7F2a^29iOE%sAua_s|;D_@a5(7pk+^m_XOp)~sBMJ((z z8l@j|-Ap6teCd12`Ld5w{*R*gPD8$UY`*kyr(qvqOd8(X!ii49cR;*->2qr|5*OB_ v(b&td)5wi;{Yry4nneB%O#fk4H@*QbOa}pU59*4R00000NkvXXu0mjfbT*oI delta 335 zcmV-V0kHn}0_g%miBL{Q4GJ0x0000DNk~Le0000I0000t2m=5B05);z-~a#s6;Mo6 zMNDaNb4f8?G$At~7m;5;p>tQbh--SnBc72(C4cQnL_t(I%cYYG4#OY}L$MRM|B>f} zmQUABs}`x1cnyfyZYhrnxU37RZx9Vls4h$`wYn$=5vWNAVx^WYNIMZhx{5duwGp6t z^tQ7}`_u^a9c`x1y~bI^%yN_2dVjmD@6pfdypx7&$0kkhpzo+sb90rIo_cXPE|qa9 zJ%1&HtnhenFhlL)MA@#*L^<+V { + const encounter = scene.currentBattle.mysteryEncounter; + + // Calculate boss mon + const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 0.25, + pokemonConfigs: [ + { + species: volcaronaSpecies, + isBoss: false, + gender: Gender.MALE + }, + { + species: volcaronaSpecies, + isBoss: false, + gender: Gender.FEMALE + } + ], + doubleBattle: true, + disableSwitch: true + }; + encounter.enemyPartyConfigs = [config]; + + const spriteKey = volcaronaSpecies.getSpriteId(false, null, false, null); + encounter.spriteConfigs = [ + { + spriteKey: spriteKey, + fileRoot: "pokemon", + tint: 0.9, + repeat: true + } + ]; + + // Sets weather for 5 turns + scene.arena.trySetWeather(WeatherType.SUNNY, true); + + return true; + }) + .withTitle(`${namespace}_title`) + .withDescription(`${namespace}_description`) + .withQuery(`${namespace}_query`) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_1_label`, + buttonTooltip: `${namespace}_option_1_tooltip`, + selected: [ + { + text: `${namespace}_option_1_selected`, + }, + ], + }, + async (scene: BattleScene) => { + // Pick battle + const encounter = scene.currentBattle.mysteryEncounter; + // TODO: play heat wave animation for weather effect + // await initMoveAnim(scene, Moves.HEAT_WAVE); + // await loadMoveAnimAssets(scene, [ Moves.HEAT_WAVE ], true); + // const heatWave = new MoveAnim(Moves.HEAT_WAVE, scene.getPlayerPokemon(), 0); + // heatWave.play(scene); + + await initMoveAnim(scene, Moves.QUIVER_DANCE); + await initMoveAnim(scene, Moves.FIRE_SPIN); + await initMoveAnim(scene, Moves.HEAT_WAVE); + const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); + encounter.startOfBattleEffects.push( + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.PLAYER, BattlerIndex.PLAYER_2], + move: new PokemonMove(Moves.HEAT_WAVE), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.ENEMY], + move: new PokemonMove(Moves.QUIVER_DANCE), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY_2, + targets: [BattlerIndex.ENEMY_2], + move: new PokemonMove(Moves.QUIVER_DANCE), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.PLAYER], + move: new PokemonMove(Moves.FIRE_SPIN), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY_2, + targets: [BattlerIndex.PLAYER_2], + move: new PokemonMove(Moves.FIRE_SPIN), + ignorePp: true + }); + await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); + } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + selected: [ + { + text: `${namespace}_option_2_selected`, + }, + ], + }, + async (scene: BattleScene) => { + // Damage party and burn 1 random member + // No rewards + leaveEncounterWithoutBattle(scene); + } + ) + .withOption( + new MysteryEncounterOptionBuilder() + .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, 2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withDialogue({ + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + selected: [ + { + text: `${namespace}_option_3_selected`, + }, + ], + }) + .withOptionPhase(async (scene: BattleScene) => { + // Fire types help calm the Volcarona + // const encounter = scene.currentBattle.mysteryEncounter; + const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); + setEncounterExp(scene, scene.getParty().map(p => p.id), 500); + leaveEncounterWithoutBattle(scene); + }) + .build() + ) + .build(); diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 2b7a013dd22..05cf76fae5f 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -304,14 +304,14 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); scene.time.delayedCall(512, () => { + scene.playSound("pb_throw"); + // Trainer throw frames scene.trainer.setFrame("2"); - scene.time.delayedCall(256, () => { + scene.time.delayedCall(184, () => { scene.trainer.setFrame("3"); scene.time.delayedCall(768, () => { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); @@ -366,18 +366,18 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); scene.time.delayedCall(512, () => { + scene.playSound("pb_throw"); + // Trainer throw frames scene.trainer.setFrame("2"); - scene.time.delayedCall(256, () => { + scene.time.delayedCall(184, () => { scene.trainer.setFrame("3"); scene.time.delayedCall(768, () => { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); @@ -395,32 +395,39 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise { + scene.time.delayedCall(200, () => { mud.setFrame("0003.png"); - scene.time.delayedCall(512, () => { + scene.time.delayedCall(400, () => { mud.setFrame("0004.png"); }); }); - scene.time.delayedCall(1536, () => { - mud.destroy(); - scene.tweens.add({ - targets: pokemon, - duration: 300, - ease: "Cubic.easeOut", - yoyo: true, - y: originalY - 20, - loop: 1, - onStart: () => { - scene.playSound("PRSFX- Taunt2"); - }, - onLoop: () => { - scene.playSound("PRSFX- Taunt2"); - }, - onComplete: () => { - resolve(true); - } - }); + // Fade mud then angry animation + scene.tweens.add({ + targets: mud, + alpha: 0, + ease: "Cubic.easeIn", + duration: 1000, + onComplete: () => { + mud.destroy(); + scene.tweens.add({ + targets: pokemon, + duration: 300, + ease: "Cubic.easeOut", + yoyo: true, + y: originalY - 20, + loop: 1, + onStart: () => { + scene.playSound("PRSFX- Taunt2"); + }, + onLoop: () => { + scene.playSound("PRSFX- Taunt2"); + }, + onComplete: () => { + resolve(true); + } + }); + } }); } }); diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index c0c8b1e5aaf..608783b7d87 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -1,5 +1,5 @@ import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; +import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { isNullOrUndefined } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../battle-scene"; @@ -18,6 +18,7 @@ import { StatusEffectRequirement, WaveRangeRequirement } from "./mystery-encounter-requirements"; +import { BattlerIndex } from "#app/battle"; export enum MysteryEncounterVariant { DEFAULT, @@ -36,6 +37,15 @@ export enum MysteryEncounterTier { MASTER // Not currently used } +export class StartOfBattleEffect { + sourcePokemon?: Pokemon; + sourceBattlerIndex?: BattlerIndex; + targets: BattlerIndex[]; + move: PokemonMove; + followUp?: boolean; + ignorePp?: boolean; +} + export default interface IMysteryEncounter { /** * Required params @@ -90,6 +100,15 @@ export default interface IMysteryEncounter { * You probably shouldn't do anything with this unless you have a very specific need */ introVisuals?: MysteryEncounterIntroVisuals; + /** + * Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave + * You should never need to modify this + */ + seedOffset?: any; + /** + * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases + */ + startOfBattleEffectsComplete?: boolean; /** * Flags @@ -116,16 +135,15 @@ export default interface IMysteryEncounter { * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases */ selectedOption?: MysteryEncounterOption; + /** + * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases + */ + startOfBattleEffects?: StartOfBattleEffect[]; /** * Can be set higher or lower based on the type of battle or exp gained for an option/encounter * Defaults to 1 */ expMultiplier?: number; - /** - * Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave - * You should never need to modify this - */ - seedOffset?: any; /** * Generic property to set any custom data required for the encounter * Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase @@ -151,8 +169,10 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true; + this.startOfBattleEffects = this.startOfBattleEffects ?? []; // Reset any dirty flags or encounter data + this.startOfBattleEffectsComplete = false; this.lockEncounterRewardTiers = true; this.dialogueTokens = {}; this.enemyPartyConfigs = []; diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index a53f12a0bfa..c3016d8aaa2 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -11,6 +11,7 @@ import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; import { FieldTripEncounter } from "./encounters/field-trip-encounter"; import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; +import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; @@ -184,7 +185,9 @@ export const mysteryEncountersByBiome = new Map([ [Biome.ICE_CAVE, []], [Biome.MEADOW, []], [Biome.POWER_PLANT, []], - [Biome.VOLCANO, []], + [Biome.VOLCANO, [ + MysteryEncounterType.FIERY_FALLOUT + ]], [Biome.GRAVEYARD, []], [Biome.DOJO, []], [Biome.FACTORY, []], @@ -215,6 +218,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter; allMysteryEncounters[MysteryEncounterType.SAFARI_ZONE] = SafariZoneEncounter; + allMysteryEncounters[MysteryEncounterType.FIERY_FALLOUT] = FieryFalloutEncounter; // Add extreme encounters to biome map extremeBiomeEncounters.forEach(encounter => { diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c9c3e980456..c38caee472b 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,5 +1,5 @@ import i18next from "i18next"; -import { BattleType } from "#app/battle"; +import { BattlerIndex, BattleType } from "#app/battle"; import BattleScene from "../../../battle-scene"; import PokemonSpecies from "../../pokemon-species"; import { MysteryEncounterVariant } from "../mystery-encounter"; @@ -9,7 +9,7 @@ import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; import Trainer, { TrainerVariant } from "../../../field/trainer"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { BattleEndPhase, EggLapsePhase, ExpPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; +import { BattleEndPhase, EggLapsePhase, ExpPhase, ModifierRewardPhase, MovePhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; import { MysteryEncounterBattlePhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import * as Utils from "../../../utils"; import { isNullOrUndefined } from "#app/utils"; @@ -25,6 +25,7 @@ import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/myst import * as Overrides from "#app/overrides"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { Gender } from "#app/data/gender"; export class EnemyPokemonConfig { species: PokemonSpecies; @@ -33,6 +34,7 @@ export class EnemyPokemonConfig { bossSegmentModifier?: number; // Additive to the determined segment number formIndex?: number; level?: number; + gender?: Gender; modifierTypes?: PokemonHeldItemModifierType[]; dataSource?: PokemonData; tags?: BattlerTagType[]; @@ -48,6 +50,7 @@ export class EnemyPartyConfig { trainerConfig?: TrainerConfig; // More customizable option for configuring trainer battle pokemonConfigs?: EnemyPokemonConfig[]; female?: boolean; // True for female trainer, false for male + disableSwitch?: boolean; // True will prevent player from switching } /** @@ -161,6 +164,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.formIndex = config.formIndex; } + // Set gender + if (!isNullOrUndefined(config.gender)) { + enemyPokemon.gender = config.gender; + } + // Set Boss if (config.isBoss) { let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); @@ -201,7 +209,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats); }); - scene.pushPhase(new MysteryEncounterBattlePhase(scene)); + scene.pushPhase(new MysteryEncounterBattlePhase(scene, partyConfig.disableSwitch)); await Promise.all(loadEnemyAssets); battle.enemyParty.forEach((enemyPokemon_2, e_1) => { @@ -562,6 +570,36 @@ export function hideMysteryEncounterIntroVisuals(scene: BattleScene): Promise { + let source; + if (effect.sourcePokemon) { + source = effect.sourcePokemon; + } else if (!isNullOrUndefined(effect.sourceBattlerIndex)) { + if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) { + source = scene.getEnemyField()[0]; + } else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) { + source = scene.getEnemyField()[0]; + } else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) { + source = scene.getEnemyField()[1]; + } else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) { + source = scene.getPlayerField()[0]; + } else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) { + source = scene.getPlayerField()[1]; + } + } else { + source = scene.getEnemyField()[0]; + } + scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.followUp)); + }); + + encounter.startOfBattleEffectsComplete = true; + } +} + /** * TODO: remove once encounter spawn rate is finalized * Just a helper function to calculate aggregate stats for MEs in a Classic run diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index fdbb957161f..15bf282d4df 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -167,7 +167,6 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon, pokeball.setOrigin(0.5, 0.625); scene.field.add(pokeball); - scene.playSound("pb_throw"); scene.time.delayedCall(300, () => { scene.field.moveBelow(pokeball as Phaser.GameObjects.GameObject, pokemon); }); @@ -175,6 +174,8 @@ export function trainerThrowPokeball(scene: BattleScene, pokemon: EnemyPokemon, return new Promise(resolve => { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); scene.time.delayedCall(512, () => { + scene.playSound("pb_throw"); + // Trainer throw frames scene.trainer.setFrame("2"); scene.time.delayedCall(256, () => { diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 2819b0eb6fb..de15b50f2b8 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -8,5 +8,6 @@ export enum MysteryEncounterType { DEPARTMENT_STORE_SALE, SHADY_VITAMIN_DEALER, FIELD_TRIP, - SAFARI_ZONE + SAFARI_ZONE, + FIERY_FALLOUT } diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index 67b2a6c8579..68d6877f53d 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -172,6 +172,26 @@ export const mysteryEncounter: SimpleTranslationEntries = { "safari_zone_pokemon_beside_itself_angry": "{{pokemonName}} is beside itself with anger!", "safari_zone_remaining_count": "{{remainingCount}} Pokémon remaining!", + "fiery_fallout_intro_message": "You encounter a blistering storm of smoke and ash!", + "fiery_fallout_title": "Fiery Fallout", + "fiery_fallout_description": "The whirling storm of ash and embers has cut visibility to nearly zero. It seems like there might be some... source that is causing these conditions. But what could be behind a phenomenon of this magnitude?", + "fiery_fallout_query": "What will you do?", + "fiery_fallout_option_1_label": "Find the source", + "fiery_fallout_option_1_tooltip": "(?) Discover the source\n(-) Hard Battle", + "fiery_fallout_option_2_label": "Hunker down", + "fiery_fallout_option_2_tooltip": "(-) Suffer the effects of the weather", + "fiery_fallout_option_3_label": "Your Fire types help", + "fiery_fallout_option_3_tooltip": "(+) End the conditions\n(+) Gain a Charcoal", + "fiery_fallout_option_3_disabled_tooltip": "You need at least 2 Fire Type Pokémon to choose this", + "fiery_fallout_option_1_selected": `You push through the storm, and find two Volcarona in the middle of a mating dance! + $They don't take kindly to the interruption and attack!`, + "fiery_fallout_option_2_selected": `The weather effects cause significant harm as you struggle to find shelter! + $Your party takes 30% Max HP damage! + $Your {burnTarget} also becomes burned!`, + // "fiery_fallout_boss_enraged": "The opposing {{enemyPokemon}} has become enraged!", + "fiery_fallout_option_3_selected": `Your {{primaryPokemonName}} and {{secondaryPokemonName}} guide you to where two Volcarona are in the middle of a mating dance! + $Thankfully, your Pokémon are able to calm them, and they depart without issue.`, + // Mystery Encounters -- Ultra Tier "training_session_intro_message": "You've come across some\ntraining tools and supplies.", diff --git a/src/overrides.ts b/src/overrides.ts index e19a5bf20dd..f7fa3c5fc86 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.FIERY_FALLOUT; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/phases.ts b/src/phases.ts index f98c914e320..5bc1a72bdf4 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1,15 +1,16 @@ import BattleScene, { bypassLogin } from "./battle-scene"; -import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; +import { DamageResult, default as Pokemon, EnemyPokemon, FieldPosition, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./field/pokemon"; import * as Utils from "./utils"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr } from "./data/move"; +import { isNullOrUndefined } from "./utils"; +import { allMoves, applyFilteredMoveAttrs, applyMoveAttrs, AttackMove, BypassRedirectAttr, BypassSleepAttr, ChargeAttr, CopyMoveAttr, FixedDamageAttr, ForceSwitchOutAttr, getMoveTargets, HealStatusEffectAttr, HitsTagAttr, IgnoreOpponentStatChangesAttr, IncrementMovePriorityAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveEffectTrigger, MoveFlags, MoveTarget, MoveTargetSet, MultiHitAttr, NoEffectAttr, OneHitKOAccuracyAttr, OverrideMoveEffectAttr, PostVictoryStatChangeAttr, PreMoveMessageAttr, SelfStatusMove, VariableAccuracyAttr, VariableTargetAttr } from "./data/move"; import { Mode } from "./ui/ui"; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; -import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier } from "./modifier/modifier"; +import { BerryModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, IvScannerModifier, LapsingPersistentModifier, LapsingPokemonHeldItemModifier, MapModifier, Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier } from "./modifier/modifier"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; -import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims"; -import { StatusEffect, getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./data/status-effect"; +import { CommonAnim, CommonBattleAnim, initMoveAnim, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims"; +import { getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, StatusEffect } from "./data/status-effect"; import { SummaryUiMode } from "./ui/summary-ui-handler"; import EvolutionSceneHandler from "./ui/evolution-scene-handler"; import { EvolutionPhase } from "./evolution-phase"; @@ -17,21 +18,21 @@ import { Phase } from "./phase"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat"; import { biomeLinks, getBiomeName } from "./data/biomes"; import { ModifierTier } from "./modifier/modifier-tier"; -import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds, CustomModifierSettings } from "./modifier/modifier-type"; +import { CustomModifierSettings, FusePokemonModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, modifierTypes, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, regenerateModifierPoolThresholds, RememberMoveModifierType, TmModifierType } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, ProtectedTag, SemiInvulnerableTag, TrappedTag, MysteryEncounterPostSummonTag } from "./data/battler-tags"; +import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, MysteryEncounterPostSummonTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags"; import { getPokemonMessage, getPokemonNameWithAffix } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; -import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; +import { getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage, Weather, WeatherType } from "./data/weather"; import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, WonderSkinAbAttr, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; -import { BattleType, BattlerIndex, TurnCommand } from "./battle"; -import { ChallengeAchv, HealAchv, LevelAchv, achvs } from "./system/achv"; -import { TrainerSlot, trainerConfigs } from "./data/trainer-config"; +import { BattlerIndex, BattleType, TurnCommand } from "./battle"; +import { achvs, ChallengeAchv, HealAchv, LevelAchv } from "./system/achv"; +import { trainerConfigs, TrainerSlot } from "./data/trainer-config"; import { EggHatchPhase } from "./egg-hatch-phase"; import { Egg } from "./data/egg"; import { vouchers } from "./system/voucher"; @@ -41,7 +42,7 @@ import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms"; import { battleSpecDialogue, getCharVariantFromDialogue, miscDialogue } from "./data/dialogue"; import { SettingKeys } from "./system/settings/settings"; -import { Tutorial, handleTutorial } from "./tutorial"; +import { handleTutorial, Tutorial } from "./tutorial"; import { TerrainType } from "./data/terrain"; import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler"; import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler"; @@ -50,7 +51,7 @@ import { GameMode, GameModes, getGameMode } from "./game-mode"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; import i18next from "./plugins/i18n"; import * as Overrides from "./overrides"; -import { TextStyle, addTextObject } from "./ui/text"; +import { addTextObject, TextStyle } from "./ui/text"; import { Type } from "./data/type"; import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene"; import { Abilities } from "#enums/abilities"; @@ -66,9 +67,8 @@ import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; -import { handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; -import { isNullOrUndefined } from "./utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; const { t } = i18next; @@ -891,6 +891,11 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer.loadAssets().then(() => battle.trainer.initSprite())); } else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { + // this.scene.getEnemyParty().forEach(p => { + // this.scene.field.remove(p); + // p.destroy(); + // }); + // this.scene.currentBattle.enemyParty = []; if (!battle.mysteryEncounter) { const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter); battle.mysteryEncounter = newEncounter; @@ -2034,6 +2039,9 @@ export class TurnInitPhase extends FieldPhase { //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); + // Start of battle effects for Mystery Encounters + handleEncounterStartOfBattleEffects(this.scene); + this.scene.getField().forEach((pokemon, i) => { if (pokemon?.isActive()) { if (pokemon.isPlayer()) { diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phase.ts index 13856471e4d..fda0e008484 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phase.ts @@ -173,8 +173,11 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { * - Queue the SummonPhases, PostSummonPhases, etc., required to initialize the phase queue for a battle */ export class MysteryEncounterBattlePhase extends Phase { - constructor(scene: BattleScene) { + disableSwitch: boolean; + + constructor(scene: BattleScene, disableSwitch = false) { super(scene); + this.disableSwitch = disableSwitch; } start() { @@ -219,7 +222,7 @@ export class MysteryEncounterBattlePhase extends Phase { } if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) { - scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 1500); + scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 500); } else { this.endBattleSetup(scene); } @@ -302,7 +305,7 @@ export class MysteryEncounterBattlePhase extends Phase { scene.pushPhase(new ToggleDoublePositionPhase(scene, false)); } - if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE && (scene.currentBattle.waveIndex > 1 || !scene.gameMode.isDaily)) { + if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE && !this.disableSwitch) { const minPartySize = scene.currentBattle.double ? 2 : 1; if (availablePartyMembers.length > minPartySize) { scene.pushPhase(new CheckSwitchPhase(scene, 0, scene.currentBattle.double)); From 09a3167bacb224b095967fdbf3d9b460e7b65251 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Tue, 16 Jul 2024 22:51:40 -0400 Subject: [PATCH 02/27] add fiery storm animation for fiery fallout --- public/battle-anims/encounter-magma-bg.json | 70 + .../battle-anims/encounter-magma-spout.json | 1569 +++++++++++++++++ src/battle-scene.ts | 3 +- src/data/battle-anims.ts | 224 +++ .../encounters/fiery-fallout-encounter.ts | 65 +- .../mystery-encounters/mystery-encounter.ts | 12 + .../utils/encounter-phase-utils.ts | 1 + src/phases.ts | 9 +- src/phases/mystery-encounter-phase.ts | 8 +- 9 files changed, 1916 insertions(+), 45 deletions(-) create mode 100644 public/battle-anims/encounter-magma-bg.json create mode 100644 public/battle-anims/encounter-magma-spout.json diff --git a/public/battle-anims/encounter-magma-bg.json b/public/battle-anims/encounter-magma-bg.json new file mode 100644 index 00000000000..1e144b9d4f6 --- /dev/null +++ b/public/battle-anims/encounter-magma-bg.json @@ -0,0 +1,70 @@ +{ + "graphic": "Encounter Magma Bg", + "frames": [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] + ], + "frameTimedEvents": { + "0": [ + { + "frameIndex": 0, + "resourceName": "PRAS- Fire BG", + "bgX": 0, + "bgY": 0, + "opacity": 0, + "duration": 35, + "eventType": "AnimTimedAddBgEvent" + }, + { + "frameIndex": 0, + "resourceName": "", + "bgX": 0, + "bgY": 0, + "opacity": 255, + "duration": 4, + "eventType": "AnimTimedUpdateBgEvent" + } + ], + "25": [ + { + "frameIndex": 25, + "resourceName": "", + "bgX": 0, + "bgY": 0, + "opacity": 0, + "duration": 7, + "eventType": "AnimTimedUpdateBgEvent" + } + ] + }, + "position": 1, + "hue": 0 +} \ No newline at end of file diff --git a/public/battle-anims/encounter-magma-spout.json b/public/battle-anims/encounter-magma-spout.json new file mode 100644 index 00000000000..961c5ab86bf --- /dev/null +++ b/public/battle-anims/encounter-magma-spout.json @@ -0,0 +1,1569 @@ +{ + "graphic": "PRAS- Magma Storm", + "frames": [ + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 28, + -29, + -29, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 56, + -57, + -57, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 85, + -85, + -85, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 120, + "y": -56, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 144, + "y": -84, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "focus": 1 + }, + { + "x": 100, + "y": -86.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 113, + -114, + -114, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 140, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 136, + "y": -92, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "focus": 1 + }, + { + "x": 108, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "priority": 0, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 141, + -142, + -142, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -76, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "focus": 1 + }, + { + "x": 116, + "y": -88, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "focus": 1 + }, + { + "x": 128, + "y": -62.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "priority": 0, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 170, + -170, + -170, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 136, + "y": -96, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "focus": 1 + }, + { + "x": 100, + "y": -76, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 148, + "y": -66.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 7, + "opacity": 255, + "priority": 0, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 198, + -199, + -199, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 108, + "y": -92, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 120, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 144, + "y": -86.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 8, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 226, + -227, + -227, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 100, + "y": -76, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 136, + "y": -68, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "focus": 1 + }, + { + "x": 128, + "y": -94.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 9, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 255, + -255, + -255, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 100.5, + "y": -70, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 144, + "y": -66, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "focus": 1 + }, + { + "x": 126, + "y": -86.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 10, + "opacity": 255, + "priority": 0, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 223, + -224, + -224, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 6, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 191, + -192, + -192, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 5, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 159, + -160, + -160, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 4, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 127, + -128, + -128, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 3, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 95, + -96, + -96, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 2, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 63, + -64, + -64, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 1, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "tone": [ + 31, + -32, + -32, + 0 + ], + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 255, + "focus": 1 + } + ], + [ + { + "x": 0, + "y": 0, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 0, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 2 + }, + { + "x": 128, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 1, + "graphicFrame": 0, + "opacity": 255, + "locked": true, + "priority": 0, + "focus": 1 + }, + { + "x": 101, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 130, + "priority": 0, + "focus": 1 + }, + { + "x": 152, + "y": -64, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 130, + "priority": 0, + "focus": 1 + }, + { + "x": 124.5, + "y": -78.5, + "zoomX": 100, + "zoomY": 100, + "visible": true, + "target": 2, + "graphicFrame": 0, + "opacity": 140, + "focus": 1 + } + ] + ], + "frameTimedEvents": { + "0": [ + { + "frameIndex": 0, + "resourceName": "PRSFX- Magma Storm1.wav", + "volume": 100, + "pitch": 100, + "eventType": "AnimTimedSoundEvent" + } + ], + "8": [ + { + "frameIndex": 8, + "resourceName": "PRSFX- Magma Storm2.wav", + "volume": 100, + "pitch": 100, + "eventType": "AnimTimedSoundEvent" + } + ] + }, + "position": 1, + "hue": 0 +} \ No newline at end of file diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c0a3f0b090a..4ebdafd3752 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -7,7 +7,7 @@ import {Constructor, isNullOrUndefined} from "#app/utils"; import * as Utils from "./utils"; import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; -import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; +import { initCommonAnims, initEncounterAnims, initMoveAnim, loadCommonAnimAssets, loadEncounterAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { Phase } from "./phase"; import { initGameSpeed } from "./system/game-speed"; import { Arena, ArenaBase } from "./field/arena"; @@ -555,6 +555,7 @@ export default class BattleScene extends SceneBase { Promise.all([ Promise.all(loadPokemonAssets), initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)), + initEncounterAnims(this).then(() => loadEncounterAnimAssets(this, true)), Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), this.initStarterColors() ]).then(() => { diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index d4dbb8ec350..1643852f922 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -102,6 +102,11 @@ export enum CommonAnim { LOCK_ON = 2120 } +export enum EncounterAnim { + MAGMA_BG, + MAGMA_SPOUT +} + export class AnimConfig { public id: integer; public graphic: string; @@ -444,6 +449,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { export const moveAnims = new Map(); export const chargeAnims = new Map(); export const commonAnims = new Map(); +export const encounterAnims = new Map(); export function initCommonAnims(scene: BattleScene): Promise { return new Promise(resolve => { @@ -511,6 +517,21 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { }); } +export function initEncounterAnims(scene: BattleScene): Promise { + return new Promise(resolve => { + const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); + const encounterAnimIds = Utils.getEnumValues(EncounterAnim); + const encounterAnimFetches = []; + for (let ea = 0; ea < encounterAnimIds.length; ea++) { + const encounterAnimId = encounterAnimIds[ea]; + encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[ea].toLowerCase().replace(/\_/g, "-")}.json`) + .then(response => response.json()) + .then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas)))); + } + Promise.allSettled(encounterAnimFetches).then(() => resolve()); + }); +} + export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise { return new Promise(resolve => { if (chargeAnims.has(chargeAnim)) { @@ -565,6 +586,12 @@ export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): P }); } +export function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise { + return new Promise(resolve => { + loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad).then(() => resolve()); + }); +} + export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise { return new Promise(resolve => { const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat(); @@ -977,6 +1004,185 @@ export abstract class BattleAnim { } }); } + + private getGraphicFrameDataWithoutTarget(scene: BattleScene, frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map> { + const ret: Map> = new Map([ + [AnimFrameTarget.GRAPHIC, new Map() ], + [AnimFrameTarget.USER, new Map() ], + [AnimFrameTarget.TARGET, new Map() ] + ]); + + const userInitialX = 0; + const userInitialY = 0; + const userHalfHeight = 30; + const targetHalfHeight = 30; + + let g = 0; + let u = 0; + let t = 0; + + for (const frame of frames) { + let x = frame.x; + let y = frame.y; + let scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1); + const scaleY = (frame.zoomY / 100); + switch (frame.focus) { + case AnimFocus.TARGET: + x += targetInitialX - targetFocusX; + y += (targetInitialY - targetHalfHeight) - targetFocusY; + break; + case AnimFocus.USER: + x += userInitialX - userFocusX; + y += (userInitialY - userHalfHeight) - userFocusY; + break; + case AnimFocus.USER_TARGET: + const point = transformPoint(this.srcLine[0], this.srcLine[1], this.srcLine[2], this.srcLine[3], + this.dstLine[0], this.dstLine[1] - userHalfHeight, this.dstLine[2], this.dstLine[3] - targetHalfHeight, x, y); + x = point[0]; + y = point[1]; + if (frame.target === AnimFrameTarget.GRAPHIC && isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])) { + scaleX = scaleX * -1; + } + break; + } + const angle = -frame.angle; + const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; + ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); + } + + return ret; + } + + playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, callback?: Function) { + const spriteCache: SpriteCache = { + [AnimFrameTarget.GRAPHIC]: [], + [AnimFrameTarget.USER]: [], + [AnimFrameTarget.TARGET]: [] + }; + const spritePriorities: integer[] = []; + + const cleanUpAndComplete = () => { + for (const ms of Object.values(spriteCache).flat()) { + if (ms) { + ms.destroy(); + } + } + if (this.bgSprite) { + this.bgSprite.destroy(); + } + if (callback) { + callback(); + } + }; + + if (!scene.moveAnimations) { + return cleanUpAndComplete(); + } + + const anim = this.getAnim(); + + this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ]; + this.dstLine = [ 150, 75, targetInitialX, targetInitialY ]; + + let r = anim.frames.length; + let f = 0; + + scene.tweens.addCounter({ + duration: Utils.getFrameMs(3) * frameTimeMult, + repeat: anim.frames.length, + onRepeat: () => { + const spriteFrames = anim.frames[f]; + const frameData = this.getGraphicFrameDataWithoutTarget(scene, anim.frames[f], targetInitialX, targetInitialY); + const u = 0; + const t = 0; + let g = 0; + for (const frame of spriteFrames) { + if (frame.target !== AnimFrameTarget.GRAPHIC) { + console.log("Encounter animations do not support targets"); + continue; + } + + const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; + if (g === sprites.length) { + const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1); + sprites.push(newSprite); + scene.field.add(newSprite); + spritePriorities.push(1); + } + + const graphicIndex = g++; + const moveSprite = sprites[graphicIndex]; + if (spritePriorities[graphicIndex] !== frame.priority) { + spritePriorities[graphicIndex] = frame.priority; + const setSpritePriority = (priority: integer) => { + if (priority < 0) { + // Move to top of scene + scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); + } else if (priority < scene.field.getAll().length) { + // Indexes of field: + // 0 is scene background + // 1 is enemy field + scene.field.moveTo(moveSprite, priority); + } else { + setSpritePriority(-1); + } + }; + setSpritePriority(frame.priority); + } + moveSprite.setFrame(frame.graphicFrame); + //console.log(AnimFocus[frame.focus]); + + const graphicFrameData = frameData.get(frame.target).get(graphicIndex); + moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); + moveSprite.setAngle(graphicFrameData.angle); + moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); + + moveSprite.setAlpha(frame.opacity / 255); + moveSprite.setVisible(frame.visible); + moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE); + } + if (anim.frameTimedEvents.has(f)) { + for (const event of anim.frameTimedEvents.get(f)) { + r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); + } + } + const targets = Utils.getEnumValues(AnimFrameTarget); + for (const i of targets) { + const count = i === AnimFrameTarget.GRAPHIC ? g : i === AnimFrameTarget.USER ? u : t; + if (count < spriteCache[i].length) { + const spritesToRemove = spriteCache[i].slice(count, spriteCache[i].length); + for (const rs of spritesToRemove) { + if (!rs.getData("locked") as boolean) { + const spriteCacheIndex = spriteCache[i].indexOf(rs); + spriteCache[i].splice(spriteCacheIndex, 1); + if (i === AnimFrameTarget.GRAPHIC) { + spritePriorities.splice(spriteCacheIndex, 1); + } + rs.destroy(); + } + } + } + } + f++; + r--; + }, + onComplete: () => { + for (const ms of Object.values(spriteCache).flat()) { + if (ms && !ms.getData("locked")) { + ms.destroy(); + } + } + if (r) { + scene.tweens.addCounter({ + duration: Utils.getFrameMs(r), + onComplete: () => cleanUpAndComplete() + }); + } else { + cleanUpAndComplete(); + } + } + }); + } } export class CommonBattleAnim extends BattleAnim { @@ -1045,6 +1251,24 @@ export class MoveChargeAnim extends MoveAnim { } } +export class EncounterBattleAnim extends BattleAnim { + public encounterAnim: EncounterAnim; + + constructor(encounterAnim: EncounterAnim, user: Pokemon, target?: Pokemon) { + super(user, target || user); + + this.encounterAnim = encounterAnim; + } + + getAnim(): AnimConfig { + return encounterAnims.get(this.encounterAnim); + } + + isOppAnim(): boolean { + return false; + } +} + export async function populateAnims() { const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase()); const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, "")); diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 8db4db8169d..84cf0d9ae7f 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -12,8 +12,9 @@ import { Type } from "#app/data/type"; import { BattlerIndex } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; -import { initMoveAnim } from "#app/data/battle-anims"; +import { EncounterAnim, EncounterBattleAnim, initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { WeatherType } from "#app/data/weather"; +import { randSeedInt } from "#app/utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fiery_fallout"; @@ -34,7 +35,7 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOnInit((scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; - // Calculate boss mon + // Calculate boss mons const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); const config: EnemyPartyConfig = { levelAdditiveMultiplier: 0.25, @@ -55,19 +56,28 @@ export const FieryFalloutEncounter: IMysteryEncounter = }; encounter.enemyPartyConfigs = [config]; - const spriteKey = volcaronaSpecies.getSpriteId(false, null, false, null); - encounter.spriteConfigs = [ - { - spriteKey: spriteKey, - fileRoot: "pokemon", - tint: 0.9, - repeat: true - } - ]; - // Sets weather for 5 turns scene.arena.trySetWeather(WeatherType.SUNNY, true); + // Load animations/sfx for Volcarona moves + Promise.all([initMoveAnim(scene, Moves.QUIVER_DANCE), initMoveAnim(scene, Moves.FIRE_SPIN)]) + .then(() => loadMoveAnimAssets(scene, [Moves.QUIVER_DANCE, Moves.FIRE_SPIN])); + + return true; + }) + .withOnVisualsStart((scene: BattleScene) => { + // Play animations + const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + background.playWithoutTargets(scene, 200, 70, 2); + const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); + animation.playWithoutTargets(scene, 200, 70, 2); + const increment = 600; + for (let i = 3; i < 6; i++) { + scene.time.delayedCall((increment) * (i - 2), () => { + animation.playWithoutTargets(scene, 100 + randSeedInt(12) * 20, 110 - randSeedInt(10) * 15, 2); + }); + } + return true; }) .withTitle(`${namespace}_title`) @@ -86,22 +96,19 @@ export const FieryFalloutEncounter: IMysteryEncounter = async (scene: BattleScene) => { // Pick battle const encounter = scene.currentBattle.mysteryEncounter; - // TODO: play heat wave animation for weather effect - // await initMoveAnim(scene, Moves.HEAT_WAVE); - // await loadMoveAnimAssets(scene, [ Moves.HEAT_WAVE ], true); - // const heatWave = new MoveAnim(Moves.HEAT_WAVE, scene.getPlayerPokemon(), 0); - // heatWave.play(scene); - - await initMoveAnim(scene, Moves.QUIVER_DANCE); - await initMoveAnim(scene, Moves.FIRE_SPIN); - await initMoveAnim(scene, Moves.HEAT_WAVE); const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); encounter.startOfBattleEffects.push( { sourceBattlerIndex: BattlerIndex.ENEMY, - targets: [BattlerIndex.PLAYER, BattlerIndex.PLAYER_2], - move: new PokemonMove(Moves.HEAT_WAVE), + targets: [BattlerIndex.PLAYER], + move: new PokemonMove(Moves.FIRE_SPIN), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY_2, + targets: [BattlerIndex.PLAYER_2], + move: new PokemonMove(Moves.FIRE_SPIN), ignorePp: true }, { @@ -115,18 +122,6 @@ export const FieryFalloutEncounter: IMysteryEncounter = targets: [BattlerIndex.ENEMY_2], move: new PokemonMove(Moves.QUIVER_DANCE), ignorePp: true - }, - { - sourceBattlerIndex: BattlerIndex.ENEMY, - targets: [BattlerIndex.PLAYER], - move: new PokemonMove(Moves.FIRE_SPIN), - ignorePp: true - }, - { - sourceBattlerIndex: BattlerIndex.ENEMY_2, - targets: [BattlerIndex.PLAYER_2], - move: new PokemonMove(Moves.FIRE_SPIN), - ignorePp: true }); await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); } diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 608783b7d87..d68e912fdff 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -64,6 +64,7 @@ export default interface IMysteryEncounter { doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; + onVisualsStart?: (scene: BattleScene) => boolean; /** * Requirements @@ -384,6 +385,7 @@ export class MysteryEncounterBuilder implements Partial { doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; + onVisualsStart?: (scene: BattleScene) => boolean; hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; enemyPartyConfigs?: EnemyPartyConfig[] = []; @@ -598,6 +600,16 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { onInit: onInit }); } + /** + * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in + * + * @param onVisualsStart - synchronous callback function to perform as soon as the enemy field finishes sliding in + * @returns + */ + withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required> { + return Object.assign(this, { onVisualsStart: onVisualsStart }); + } + /** * Defines any enemies to use for a battle from the mystery encounter * @param enemyPartyConfig diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c38caee472b..0aae84a7a8a 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -202,6 +202,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: } enemyPokemon.initBattleInfo(); + enemyPokemon.getBattleInfo().initInfo(enemyPokemon); } loadEnemyAssets.push(enemyPokemon.loadAssets()); diff --git a/src/phases.ts b/src/phases.ts index 5bc1a72bdf4..6465d573622 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -891,11 +891,6 @@ export class EncounterPhase extends BattlePhase { if (battle.battleType === BattleType.TRAINER) { loadEnemyAssets.push(battle.trainer.loadAssets().then(() => battle.trainer.initSprite())); } else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { - // this.scene.getEnemyParty().forEach(p => { - // this.scene.field.remove(p); - // p.destroy(); - // }); - // this.scene.currentBattle.enemyParty = []; if (!battle.mysteryEncounter) { const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter); battle.mysteryEncounter = newEncounter; @@ -1081,6 +1076,10 @@ export class EncounterPhase extends BattlePhase { const introVisuals = this.scene.currentBattle.mysteryEncounter.introVisuals; introVisuals.playAnim(); + if (this.scene.currentBattle.mysteryEncounter.onVisualsStart) { + this.scene.currentBattle.mysteryEncounter.onVisualsStart(this.scene); + } + const doEncounter = () => { this.scene.playBgm(undefined); diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phase.ts index fda0e008484..5745e2c68d1 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phase.ts @@ -108,9 +108,9 @@ export class MysteryEncounterPhase extends Phase { } if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0); + this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 300 : 0); } else { - this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true); + this.scene.ui.showText(text, null, nextAction, i === 0 ? 300 : 0, true); } i++; }; @@ -222,7 +222,7 @@ export class MysteryEncounterBattlePhase extends Phase { } if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) { - scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 500); + scene.ui.showText(this.getBattleMessage(scene), null, () => this.endBattleSetup(scene), 0); } else { this.endBattleSetup(scene); } @@ -243,7 +243,7 @@ export class MysteryEncounterBattlePhase extends Phase { this.endBattleSetup(scene); }; if (!scene.currentBattle.mysteryEncounter.hideBattleIntroMessage) { - scene.ui.showText(this.getBattleMessage(scene), null, doTrainerSummon, 1500, true); + scene.ui.showText(this.getBattleMessage(scene), null, doTrainerSummon, 1000, true); } else { doTrainerSummon(); } From 06684e06a2fcfa088fb053c6b81c51bbd54e8295 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Wed, 17 Jul 2024 22:58:34 -0400 Subject: [PATCH 03/27] clean up battle animation logic --- .../battle-anims/encounter-magma-spout.json | 780 +----------------- src/battle-scene.ts | 3 +- src/data/battle-anims.ts | 107 ++- .../encounters/fiery-fallout-encounter.ts | 14 +- .../mystery-encounters/mystery-encounter.ts | 20 +- .../utils/encounter-phase-utils.ts | 16 + src/phases.ts | 7 +- .../phases/mystery-encounter-phase.test.ts | 4 +- 8 files changed, 151 insertions(+), 800 deletions(-) diff --git a/public/battle-anims/encounter-magma-spout.json b/public/battle-anims/encounter-magma-spout.json index 961c5ab86bf..9e0acea61fa 100644 --- a/public/battle-anims/encounter-magma-spout.json +++ b/public/battle-anims/encounter-magma-spout.json @@ -2,32 +2,6 @@ "graphic": "PRAS- Magma Storm", "frames": [ [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -37,7 +11,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -49,7 +23,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -65,32 +39,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -100,7 +48,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -112,7 +60,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -128,32 +76,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -163,7 +85,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -175,7 +97,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -191,32 +113,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -226,7 +122,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -238,7 +134,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -254,32 +150,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -289,7 +159,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -301,7 +171,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -317,32 +187,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -352,7 +196,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -364,7 +208,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -380,38 +224,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 28, - -29, - -29, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -421,7 +233,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -433,7 +245,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -449,38 +261,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 56, - -57, - -57, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -490,7 +270,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -502,7 +282,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -518,38 +298,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 85, - -85, - -85, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 120, "y": -56, @@ -559,7 +307,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -586,38 +334,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 113, - -114, - -114, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 140, "y": -64, @@ -627,7 +343,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -650,43 +366,11 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 141, - -142, - -142, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 152, "y": -76, @@ -718,43 +402,11 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 170, - -170, - -170, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 136, "y": -96, @@ -775,7 +427,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -787,43 +439,11 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 198, - -199, - -199, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 108, "y": -92, @@ -833,7 +453,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -845,7 +465,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -861,38 +481,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 226, - -227, - -227, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 100, "y": -76, @@ -902,7 +490,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -929,38 +517,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 255, - -255, - -255, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 100.5, "y": -70, @@ -970,7 +526,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -993,43 +549,11 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 223, - -224, - -224, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1039,7 +563,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1051,7 +575,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1067,38 +591,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 191, - -192, - -192, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1108,7 +600,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1120,7 +612,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1136,38 +628,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 159, - -160, - -160, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1177,7 +637,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1189,7 +649,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1205,38 +665,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 127, - -128, - -128, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1246,7 +674,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1258,7 +686,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1274,38 +702,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 95, - -96, - -96, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1315,7 +711,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1327,7 +723,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1343,38 +739,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 63, - -64, - -64, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1384,7 +748,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1396,7 +760,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1412,38 +776,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "tone": [ - 31, - -32, - -32, - 0 - ], - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1453,7 +785,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1465,7 +797,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1481,32 +813,6 @@ } ], [ - { - "x": 0, - "y": 0, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 0, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 2 - }, - { - "x": 128, - "y": -64, - "zoomX": 100, - "zoomY": 100, - "visible": true, - "target": 1, - "graphicFrame": 0, - "opacity": 255, - "locked": true, - "priority": 0, - "focus": 1 - }, { "x": 101, "y": -64, @@ -1516,7 +822,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 0, + "priority": 1, "focus": 1 }, { @@ -1528,7 +834,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 0, + "priority": 1, "focus": 1 }, { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 4ebdafd3752..c0a3f0b090a 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -7,7 +7,7 @@ import {Constructor, isNullOrUndefined} from "#app/utils"; import * as Utils from "./utils"; import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; -import { initCommonAnims, initEncounterAnims, initMoveAnim, loadCommonAnimAssets, loadEncounterAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; +import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { Phase } from "./phase"; import { initGameSpeed } from "./system/game-speed"; import { Arena, ArenaBase } from "./field/arena"; @@ -555,7 +555,6 @@ export default class BattleScene extends SceneBase { Promise.all([ Promise.all(loadPokemonAssets), initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)), - initEncounterAnims(this).then(() => loadEncounterAnimAssets(this, true)), Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), this.initStarterColors() ]).then(() => { diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 1643852f922..f832318fc4b 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -6,6 +6,7 @@ import * as Utils from "../utils"; import { BattlerIndex } from "../battle"; import { Element } from "json-stable-stringify"; import { Moves } from "#enums/moves"; +import { isNullOrUndefined } from "../utils"; //import fs from 'vite-plugin-fs/browser'; export enum AnimFrameTarget { @@ -307,7 +308,7 @@ abstract class AnimTimedEvent { this.resourceName = resourceName; } - abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer; + abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer; abstract getEventType(): string; } @@ -325,7 +326,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent { } } - execute(scene: BattleScene, battleAnim: BattleAnim): integer { + execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer { const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) }; if (this.resourceName) { try { @@ -387,7 +388,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { super(frameIndex, resourceName, source); } - execute(scene: BattleScene, moveAnim: MoveAnim): integer { + execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { const tweenProps = {}; if (this.bgX !== undefined) { tweenProps["x"] = (this.bgX * 0.5) - 320; @@ -417,7 +418,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { super(frameIndex, resourceName, source); } - execute(scene: BattleScene, moveAnim: MoveAnim): integer { + execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { if (moveAnim.bgSprite) { moveAnim.bgSprite.destroy(); } @@ -429,7 +430,9 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { moveAnim.bgSprite.setAlpha(this.opacity / 255); scene.field.add(moveAnim.bgSprite); const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon(); - if (fieldPokemon?.isOnField()) { + if (!isNullOrUndefined(priority)) { + scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority); + } else if (fieldPokemon?.isOnField()) { scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); } @@ -517,14 +520,18 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { }); } -export function initEncounterAnims(scene: BattleScene): Promise { +export function initEncounterAnims(scene: BattleScene, anims: EncounterAnim | EncounterAnim[]): Promise { + anims = anims instanceof Array ? anims : [anims]; return new Promise(resolve => { const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimIds = Utils.getEnumValues(EncounterAnim); const encounterAnimFetches = []; - for (let ea = 0; ea < encounterAnimIds.length; ea++) { - const encounterAnimId = encounterAnimIds[ea]; - encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[ea].toLowerCase().replace(/\_/g, "-")}.json`) + for (const anim of anims) { + if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { + continue; + } + const encounterAnimId = encounterAnimIds[anim]; + encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) .then(response => response.json()) .then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas)))); } @@ -1005,18 +1012,13 @@ export abstract class BattleAnim { }); } - private getGraphicFrameDataWithoutTarget(scene: BattleScene, frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map> { + private getGraphicFrameDataWithoutTarget(frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map> { const ret: Map> = new Map([ [AnimFrameTarget.GRAPHIC, new Map() ], [AnimFrameTarget.USER, new Map() ], [AnimFrameTarget.TARGET, new Map() ] ]); - const userInitialX = 0; - const userInitialY = 0; - const userHalfHeight = 30; - const targetHalfHeight = 30; - let g = 0; let u = 0; let t = 0; @@ -1024,27 +1026,10 @@ export abstract class BattleAnim { for (const frame of frames) { let x = frame.x; let y = frame.y; - let scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1); + const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1); const scaleY = (frame.zoomY / 100); - switch (frame.focus) { - case AnimFocus.TARGET: - x += targetInitialX - targetFocusX; - y += (targetInitialY - targetHalfHeight) - targetFocusY; - break; - case AnimFocus.USER: - x += userInitialX - userFocusX; - y += (userInitialY - userHalfHeight) - userFocusY; - break; - case AnimFocus.USER_TARGET: - const point = transformPoint(this.srcLine[0], this.srcLine[1], this.srcLine[2], this.srcLine[3], - this.dstLine[0], this.dstLine[1] - userHalfHeight, this.dstLine[2], this.dstLine[3] - targetHalfHeight, x, y); - x = point[0]; - y = point[1]; - if (frame.target === AnimFrameTarget.GRAPHIC && isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])) { - scaleX = scaleX * -1; - } - break; - } + x += targetInitialX; + y += targetInitialY; const angle = -frame.angle; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); @@ -1053,7 +1038,20 @@ export abstract class BattleAnim { return ret; } - playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, callback?: Function) { + /** + * + * @param scene + * @param targetInitialX + * @param targetInitialY + * @param frameTimeMult + * @param frameTimedEventPriority + * - 0 is behind all other sprites (except BG) + * - 1 on top of player field + * - 3 is on top of both fields + * - 5 is on top of player sprite + * @param callback + */ + playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) { const spriteCache: SpriteCache = { [AnimFrameTarget.GRAPHIC]: [], [AnimFrameTarget.USER]: [], @@ -1087,12 +1085,17 @@ export abstract class BattleAnim { let r = anim.frames.length; let f = 0; + const fieldSprites = scene.field.getAll(); + const playerFieldSprite = fieldSprites[1]; + const enemyFieldSprite = fieldSprites[3]; + const trainerSprite = fieldSprites[5]; + scene.tweens.addCounter({ duration: Utils.getFrameMs(3) * frameTimeMult, repeat: anim.frames.length, onRepeat: () => { const spriteFrames = anim.frames[f]; - const frameData = this.getGraphicFrameDataWithoutTarget(scene, anim.frames[f], targetInitialX, targetInitialY); + const frameData = this.getGraphicFrameDataWithoutTarget(anim.frames[f], targetInitialX, targetInitialY); const u = 0; const t = 0; let g = 0; @@ -1116,13 +1119,29 @@ export abstract class BattleAnim { spritePriorities[graphicIndex] = frame.priority; const setSpritePriority = (priority: integer) => { if (priority < 0) { - // Move to top of scene + // Move to top of scene scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); - } else if (priority < scene.field.getAll().length) { - // Indexes of field: - // 0 is scene background - // 1 is enemy field - scene.field.moveTo(moveSprite, priority); + } else if (priority === 1) { + // Move above player field + if (playerFieldSprite) { + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, playerFieldSprite); + } else { + setSpritePriority(-1); + } + } else if (priority === 3) { + // Move above player enemy field + if (enemyFieldSprite) { + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, enemyFieldSprite); + } else { + setSpritePriority(-1); + } + } else if (priority === 5) { + // Move above player trainer sprite + if (trainerSprite) { + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, trainerSprite); + } else { + setSpritePriority(-1); + } } else { setSpritePriority(-1); } @@ -1143,7 +1162,7 @@ export abstract class BattleAnim { } if (anim.frameTimedEvents.has(f)) { for (const event of anim.frameTimedEvents.get(f)) { - r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); + r = Math.max((anim.frames.length - f) + event.execute(scene, this, frameTimedEventPriority), r); } } const targets = Utils.getEnumValues(AnimFrameTarget); diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 84cf0d9ae7f..df637aa4cfd 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -1,5 +1,5 @@ import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { modifierTypes, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; @@ -12,7 +12,7 @@ import { Type } from "#app/data/type"; import { BattlerIndex } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; -import { EncounterAnim, EncounterBattleAnim, initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#app/data/weather"; import { randSeedInt } from "#app/utils"; @@ -27,6 +27,7 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withSceneWaveRangeRequirement(40, 180) // waves 10 to 180 .withCatchAllowed(true) .withIntroSpriteConfigs([]) // Set in onInit() + .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) .withIntroDialogue([ { text: `${namespace}_intro_message`, @@ -60,21 +61,20 @@ export const FieryFalloutEncounter: IMysteryEncounter = scene.arena.trySetWeather(WeatherType.SUNNY, true); // Load animations/sfx for Volcarona moves - Promise.all([initMoveAnim(scene, Moves.QUIVER_DANCE), initMoveAnim(scene, Moves.FIRE_SPIN)]) - .then(() => loadMoveAnimAssets(scene, [Moves.QUIVER_DANCE, Moves.FIRE_SPIN])); + initCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]); return true; }) .withOnVisualsStart((scene: BattleScene) => { // Play animations const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); - background.playWithoutTargets(scene, 200, 70, 2); + background.playWithoutTargets(scene, 200, 70, 2, 3); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); - animation.playWithoutTargets(scene, 200, 70, 2); + animation.playWithoutTargets(scene, 100, 100, 2); const increment = 600; for (let i = 3; i < 6; i++) { scene.time.delayedCall((increment) * (i - 2), () => { - animation.playWithoutTargets(scene, 100 + randSeedInt(12) * 20, 110 - randSeedInt(10) * 15, 2); + animation.playWithoutTargets(scene, randSeedInt(12) * 15, 150 - randSeedInt(10) * 15, 2); }); } diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index d68e912fdff..efad93e2f28 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -19,6 +19,7 @@ import { WaveRangeRequirement } from "./mystery-encounter-requirements"; import { BattlerIndex } from "#app/battle"; +import { EncounterAnim } from "#app/data/battle-anims"; export enum MysteryEncounterVariant { DEFAULT, @@ -57,6 +58,7 @@ export default interface IMysteryEncounter { * Optional params */ encounterTier?: MysteryEncounterTier; + encounterAnimations?: EncounterAnim[]; hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; catchAllowed?: boolean; @@ -193,13 +195,6 @@ export default class IMysteryEncounter implements IMysteryEncounter { const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene); - // console.log("-------" + MysteryEncounterType[this.encounterType] + " Encounter Check -------"); - // console.log(this); - // console.log( "sceneCheck: " + sceneReq); - // console.log( "primaryCheck: " + priReqs); - // console.log( "secondaryCheck: " + secReqs); - // console.log(MysteryEncounterTier[this.encounterTier]); - return sceneReq && secReqs && priReqs; } @@ -377,6 +372,7 @@ export class MysteryEncounterBuilder implements Partial { dialogue?: MysteryEncounterDialogue; encounterTier?: MysteryEncounterTier; + encounterAnimations?: EncounterAnim[]; requirements?: EncounterSceneRequirement[] = []; primaryPokemonRequirements?: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; @@ -471,6 +467,16 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { encounterTier: encounterTier }); } + /** + * Defines any EncounterAnim animations that are intended to be used during the encounter + * @param encounterAnimations + * @returns + */ + withAnimations(...encounterAnimations: EncounterAnim[]): this & Required> { + encounterAnimations = encounterAnimations instanceof Array ? encounterAnimations : [encounterAnimations]; + return Object.assign(this, { encounterAnimations: encounterAnimations }); + } + /** * Sets the maximum number of times that an encounter can spawn in a given Classic run * @param maxAllowedEncounters diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 0aae84a7a8a..ba6a154abd9 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -26,6 +26,8 @@ import * as Overrides from "#app/overrides"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { Gender } from "#app/data/gender"; +import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +import { Moves } from "#enums/moves"; export class EnemyPokemonConfig { species: PokemonSpecies; @@ -230,6 +232,20 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: } } +/** + * Load special move animations/sfx for hard-coded encounter-specific moves that a pokemon uses at the start of an encounter + * 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) + * @param scene + * @param moves + */ +export function initCustomMovesForEncounter(scene: BattleScene, moves: Moves | Moves[]) { + moves = moves instanceof Array ? moves : [moves]; + return Promise.all(moves.map(move => initMoveAnim(scene, move))) + .then(() => loadMoveAnimAssets(scene, moves)); +} + /** * Will update player money, and animate change (sound optional) * @param scene - Battle Scene diff --git a/src/phases.ts b/src/phases.ts index 6465d573622..015a97ca115 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -9,7 +9,7 @@ import { Stat } from "./data/pokemon-stat"; import { BerryModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, IvScannerModifier, LapsingPersistentModifier, LapsingPokemonHeldItemModifier, MapModifier, Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier } from "./modifier/modifier"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; -import { CommonAnim, CommonBattleAnim, initMoveAnim, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims"; +import { CommonAnim, CommonBattleAnim, initEncounterAnims, initMoveAnim, loadEncounterAnimAssets, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims"; import { getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, StatusEffect } from "./data/status-effect"; import { SummaryUiMode } from "./ui/summary-ui-handler"; import EvolutionSceneHandler from "./ui/evolution-scene-handler"; @@ -832,6 +832,11 @@ export class EncounterPhase extends BattlePhase { mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); }, this.scene.currentBattle.waveIndex); + // Add any special encounter animations to load + if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) { + loadEnemyAssets.push(initEncounterAnims(this.scene, mysteryEncounter.encounterAnimations).then(() => loadEncounterAnimAssets(this.scene, true))); + } + // Add intro visuals for mystery encounter mysteryEncounter.initIntroVisuals(this.scene); this.scene.field.add(mysteryEncounter.introVisuals); diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 0ae9ad35ed7..eef252bfff2 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -89,8 +89,8 @@ describe("Mystery Encounter Phases", () => { expect(dialogueSpy).toHaveBeenCalledTimes(1); expect(messageSpy).toHaveBeenCalledTimes(2); expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function)); - expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true); - expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 750, true); + expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 300, true); + expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 300, true); }); }); From 2697a738babafef7f5996936e9a8632c9905fc43 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 11:04:52 -0400 Subject: [PATCH 04/27] clean up unit tests and utils --- .../encounters/fiery-fallout-encounter.ts | 2 +- .../encounters/safari-zone-encounter.ts | 11 +-- .../mystery-encounters/mystery-encounter.ts | 14 ++-- .../utils/encounter-phase-utils.ts | 6 +- src/overrides.ts | 4 +- .../mystery-encounter/encounterTestUtils.ts | 79 ++++++++++++------- .../encounters/lost-at-sea-encounter.test.ts | 15 +--- .../mystery-encounter-utils.test.ts | 40 +++------- .../mystery-encounter.test.ts | 16 +--- .../phases/mystery-encounter-phase.test.ts | 39 +++++---- src/test/utils/TextInterceptor.ts | 5 ++ src/test/utils/gameManager.ts | 2 +- .../utils/mocks/mocksContainer/mockText.ts | 8 ++ src/test/utils/overridesHelper.ts | 55 ++++++++++--- src/test/utils/phaseInterceptor.ts | 2 +- src/test/utils/testUtils.ts | 13 --- 16 files changed, 166 insertions(+), 145 deletions(-) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index df637aa4cfd..45987c928a1 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -145,7 +145,7 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, 2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withDialogue({ buttonLabel: `${namespace}_option_3_label`, buttonTooltip: `${namespace}_option_3_tooltip`, diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 05cf76fae5f..42e4d17d518 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -57,7 +57,7 @@ export const SafariZoneEncounter: IMysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { // Start safari encounter const encounter = scene.currentBattle.mysteryEncounter; - encounter.encounterVariant = MysteryEncounterVariant.SAFARI_BATTLE; + encounter.encounterVariant = MysteryEncounterVariant.REPEATED_ENCOUNTER; encounter.misc = { safariPokemonRemaining: 3 }; @@ -463,14 +463,15 @@ function tryChangeCatchStage(scene: BattleScene, change: number, chance?: number return true; } -async function doEndTurn(scene: BattleScene, cursorIndex: number, message?: string) { - const pokemon = scene.currentBattle.mysteryEncounter.misc.pokemon; - const isFlee = isPokemonFlee(pokemon, scene.currentBattle.mysteryEncounter.misc.fleeStage); +async function doEndTurn(scene: BattleScene, cursorIndex: number) { + const encounter = scene.currentBattle.mysteryEncounter; + const pokemon = encounter.misc.pokemon; + const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage); if (isFlee) { // Pokemon flees! await doPokemonFlee(scene, pokemon); // Check how many safari pokemon left - if (scene.currentBattle.mysteryEncounter.misc.safariPokemonRemaining > 0) { + if (encounter.misc.safariPokemonRemaining > 0) { await summonSafariPokemon(scene); initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); } else { diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 489bbd84d0a..1c6ad219799 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -27,7 +27,7 @@ export enum MysteryEncounterVariant { WILD_BATTLE, BOSS_BATTLE, NO_BATTLE, - SAFARI_BATTLE + REPEATED_ENCOUNTER } export enum MysteryEncounterTier { @@ -38,7 +38,7 @@ export enum MysteryEncounterTier { MASTER // Not currently used } -export class StartOfBattleEffect { +export interface StartOfBattleEffect { sourcePokemon?: Pokemon; sourceBattlerIndex?: BattlerIndex; targets: BattlerIndex[]; @@ -108,10 +108,6 @@ export default interface IMysteryEncounter { * You should never need to modify this */ seedOffset?: any; - /** - * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases - */ - startOfBattleEffectsComplete?: boolean; /** * Flags @@ -134,6 +130,10 @@ export default interface IMysteryEncounter { * Will be set to false after a shop is shown (so can't reroll same rarity items for free) */ lockEncounterRewardTiers?: boolean; + /** + * Will be set automatically, indicates special moves in startOfBattleEffects are complete (so will not repeat) + */ + startOfBattleEffectsComplete?: boolean; /** * Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases */ @@ -473,6 +473,8 @@ export class MysteryEncounterBuilder implements Partial { /** * Defines any EncounterAnim animations that are intended to be used during the encounter + * EncounterAnims can be played at any point during an encounter or callback + * They just need to be specified here so that resources are loaded on encounter init * @param encounterAnimations * @returns */ diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 3c35263fae2..6e13bbb0874 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -555,8 +555,10 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: return; } - if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.SAFARI_BATTLE) { - scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); + // If in repeated encounter variant, do nothing + // Variant must eventually be swapped in order to handle "true" end of the encounter + if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.REPEATED_ENCOUNTER) { + return; } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) { scene.pushPhase(new EggLapsePhase(scene)); scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase)); diff --git a/src/overrides.ts b/src/overrides.ts index f7fa3c5fc86..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.FIERY_FALLOUT; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 65f6ae0edc9..1a602b7b239 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,17 +1,19 @@ import { Button } from "#app/enums/buttons"; -import { MessagePhase } from "#app/phases"; +import { MessagePhase, VictoryPhase } from "#app/phases"; import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; +import MessageUiHandler from "#app/ui/message-ui-handler"; export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { + // Handle any eventual queued messages (e.g. weather phase, etc.) + game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + if (game.isCurrentPhase(MessagePhase)) { - // Handle eventual weather messages (e.g. a downpour started!) - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.processInput(Button.ACTION); - }); await game.phaseInterceptor.run(MessagePhase); } @@ -20,35 +22,56 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); - // select the desired option - game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { - const uiHandler = game.scene.ui.getHandler(); - uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that - switch (optionNo) { - case 1: - // no movement needed. Default cursor position - break; - case 2: - uiHandler.processInput(Button.RIGHT); - break; - case 3: - uiHandler.processInput(Button.DOWN); - break; - case 4: - uiHandler.processInput(Button.RIGHT); - uiHandler.processInput(Button.DOWN); - break; - } - - uiHandler.processInput(Button.ACTION); - }); await game.phaseInterceptor.run(MysteryEncounterPhase); + // select the desired option + const uiHandler = game.scene.ui.getHandler(); + uiHandler.unblockInput(); // input are blocked by 1s to prevent accidental input. Tests need to handle that + + switch (optionNo) { + case 1: + // no movement needed. Default cursor position + break; + case 2: + uiHandler.processInput(Button.RIGHT); + break; + case 3: + uiHandler.processInput(Button.DOWN); + break; + case 4: + uiHandler.processInput(Button.RIGHT); + uiHandler.processInput(Button.DOWN); + break; + } + + uiHandler.processInput(Button.ACTION); + // run the selected options phase game.onNextPrompt("MysteryEncounterOptionSelectedPhase", Mode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); + + // If a battle is started, fast forward to end of the battle + game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + game.scene.clearPhaseQueue(); + game.scene.clearPhaseQueueSplice(); + game.scene.unshiftPhase(new VictoryPhase(game.scene, 0)); + game.endPhase(); + }); + + // Handle end of battle trainer messages + game.onNextPrompt("TrainerVictoryPhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + + // Handle egg hatch dialogue + game.onNextPrompt("EggLapsePhase", Mode.MESSAGE, () => { + const uiHandler = game.scene.ui.getHandler(); + uiHandler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); } diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 3b8017fa4c6..14dd43be8bb 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -9,7 +9,6 @@ import { Moves } from "#app/enums/moves"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { Species } from "#app/enums/species"; import GameManager from "#app/test/utils/gameManager"; -import { workaround_reInitSceneWithOverrides } from "#app/test/utils/testUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; @@ -30,8 +29,9 @@ describe("Lost at Sea - Mystery Encounter", () => { beforeEach(async () => { game = new GameManager(phaserGame); game.override.mysteryEncounterChance(100); - game.override.startingBiome(defaultBiome); game.override.startingWave(defaultWave); + game.override.startingBiome(defaultBiome); + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ [Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]], @@ -45,7 +45,6 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); @@ -59,7 +58,6 @@ describe("Lost at Sea - Mystery Encounter", () => { it("should not spawn outside of sea biome", async () => { game.override.startingBiome(Biome.MOUNTAIN); - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(); expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); @@ -117,7 +115,6 @@ describe("Lost at Sea - Mystery Encounter", () => { it("should award exp to surfable PKM (Blastoise)", async () => { const laprasSpecies = getPokemonSpecies(Species.LAPRAS); - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); const party = game.scene.getParty(); const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); @@ -126,13 +123,12 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); expect(blastoise.exp).toBe(expBefore + laprasSpecies.baseExp * defaultWave); - }); + }, 10000000); it("should leave encounter without battle", async () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); await runSelectMysteryEncounterOption(game, 1); @@ -168,7 +164,6 @@ describe("Lost at Sea - Mystery Encounter", () => { const wave = 33; game.override.startingWave(wave); - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); const party = game.scene.getParty(); const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); @@ -183,7 +178,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await workaround_reInitSceneWithOverrides(game); + // await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); await runSelectMysteryEncounterOption(game, 2); @@ -215,7 +210,6 @@ describe("Lost at Sea - Mystery Encounter", () => { it("should damage all (allowed in battle) party PKM by 25%", async () => { game.override.startingWave(33); - await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); const party = game.scene.getParty(); @@ -237,7 +231,6 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); await runSelectMysteryEncounterOption(game, 3); diff --git a/src/test/mystery-encounter/mystery-encounter-utils.test.ts b/src/test/mystery-encounter/mystery-encounter-utils.test.ts index 22582f8a8bc..2867bcc63bc 100644 --- a/src/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/src/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -36,16 +36,12 @@ describe("Mystery Encounter Utils", () => { describe("getRandomPlayerPokemon", () => { it("gets a random pokemon from player party", () => { // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) - scene.waveSeed = "random"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random"); let result = getRandomPlayerPokemon(scene); expect(result.species.speciesId).toBe(Species.MANAPHY); - scene.waveSeed = "random2"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random2"); result = getRandomPlayerPokemon(scene); expect(result.species.speciesId).toBe(Species.ARCEUS); @@ -60,16 +56,12 @@ describe("Mystery Encounter Utils", () => { }); // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) - scene.waveSeed = "random"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random"); let result = getRandomPlayerPokemon(scene); expect(result.species.speciesId).toBe(Species.MANAPHY); - scene.waveSeed = "random2"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random2"); result = getRandomPlayerPokemon(scene); expect(result.species.speciesId).toBe(Species.ARCEUS); @@ -83,16 +75,12 @@ describe("Mystery Encounter Utils", () => { party[0].updateInfo(); // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) - scene.waveSeed = "random"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random"); let result = getRandomPlayerPokemon(scene, true); expect(result.species.speciesId).toBe(Species.MANAPHY); - scene.waveSeed = "random2"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random2"); result = getRandomPlayerPokemon(scene, true); expect(result.species.speciesId).toBe(Species.MANAPHY); @@ -106,16 +94,12 @@ describe("Mystery Encounter Utils", () => { party[0].updateInfo(); // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) - scene.waveSeed = "random"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random"); let result = getRandomPlayerPokemon(scene, true, false); expect(result.species.speciesId).toBe(Species.MANAPHY); - scene.waveSeed = "random2"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random2"); result = getRandomPlayerPokemon(scene, true, false); expect(result.species.speciesId).toBe(Species.MANAPHY); @@ -129,16 +113,12 @@ describe("Mystery Encounter Utils", () => { party[0].updateInfo(); // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) - scene.waveSeed = "random"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random"); let result = getRandomPlayerPokemon(scene, true, true); expect(result.species.speciesId).toBe(Species.ARCEUS); - scene.waveSeed = "random2"; - Phaser.Math.RND.sow([scene.waveSeed]); - scene.rngCounter = 0; + game.override.seed("random2"); result = getRandomPlayerPokemon(scene, true, true); expect(result.species.speciesId).toBe(Species.ARCEUS); diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index f4a083c06d4..8ed2e4756ca 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -1,5 +1,4 @@ -import { afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vitest"; -import * as overrides from "../../overrides"; +import { afterEach, beforeAll, beforeEach, expect, describe, it } from "vitest"; import GameManager from "#app/test/utils/gameManager"; import Phaser from "phaser"; import { Species } from "#enums/species"; @@ -22,16 +21,9 @@ describe("Mystery Encounters", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11); - vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); - - // Seed guarantees wild encounter to be replaced by ME - vi.spyOn(game.scene, "resetSeed").mockImplementation(() => { - game.scene.waveSeed = "test"; - Phaser.Math.RND.sow([game.scene.waveSeed]); - game.scene.rngCounter = 0; - }); + game.override.startingWave(11); + game.override.mysteryEncounterChance(100); + game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); }); it("Spawns a mystery encounter", async () => { diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index eef252bfff2..91e6a09bb99 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -1,14 +1,14 @@ -import {afterEach, beforeAll, beforeEach, expect, describe, it, vi} from "vitest"; -import * as overrides from "../../overrides"; +import {afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vitest"; import GameManager from "#app/test/utils/gameManager"; import Phaser from "phaser"; import {Species} from "#enums/species"; -import {MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; import {Mode} from "#app/ui/ui"; import {Button} from "#enums/buttons"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import {MysteryEncounterType} from "#enums/mystery-encounter-type"; import {MysteryEncounterTier} from "#app/data/mystery-encounters/mystery-encounter"; +import MessageUiHandler from "#app/ui/message-ui-handler"; describe("Mystery Encounter Phases", () => { let phaserGame: Phaser.Game; @@ -26,16 +26,11 @@ describe("Mystery Encounter Phases", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11); - vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); - + game.override.startingWave(11); + game.override.mysteryEncounterChance(100); + game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); // Seed guarantees wild encounter to be replaced by ME - vi.spyOn(game.scene, "resetSeed").mockImplementation(() => { - game.scene.waveSeed = "test"; - Phaser.Math.RND.sow([ game.scene.waveSeed ]); - game.scene.rngCounter = 0; - }); + game.override.seed("test"); }); describe("MysteryEncounterPhase", () => { @@ -75,21 +70,25 @@ describe("Mystery Encounter Phases", () => { Species.VOLCARONA ]); - game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { - // Select option 1 for encounter - const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler; - handler.unblockInput(); + game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { + const handler = game.scene.ui.getHandler() as MessageUiHandler; handler.processInput(Button.ACTION); - }, () => !game.isCurrentPhase(MysteryEncounterPhase)); + }); + await game.phaseInterceptor.run(MysteryEncounterPhase); - // After option selected - expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name); + // Select option 1 for encounter + const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler; + handler.unblockInput(); + handler.processInput(Button.ACTION); + + // Waitfor required so that option select messages and preOptionPhase logic are handled + await vi.waitFor(() => expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name)); expect(game.scene.ui.getMode()).toBe(Mode.MESSAGE); expect(dialogueSpy).toHaveBeenCalledTimes(1); expect(messageSpy).toHaveBeenCalledTimes(2); expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function)); - expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 300, true); + expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true); expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 300, true); }); }); diff --git a/src/test/utils/TextInterceptor.ts b/src/test/utils/TextInterceptor.ts index 34b55aa30ac..a49f41f6be0 100644 --- a/src/test/utils/TextInterceptor.ts +++ b/src/test/utils/TextInterceptor.ts @@ -11,6 +11,11 @@ export default class TextInterceptor { this.logs.push(text); } + showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, promptDelay?: integer): void { + console.log(name, text); + this.logs.push(name, text); + } + getLatestMessage(): string { return this.logs.pop(); } diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 6c750c60a92..f0c7d08c6b7 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -62,7 +62,7 @@ export default class GameManager { this.phaseInterceptor = new PhaseInterceptor(this.scene); this.textInterceptor = new TextInterceptor(this.scene); this.gameWrapper.setScene(this.scene); - this.override = new OverridesHelper(); + this.override = new OverridesHelper(this); } /** diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index 1dd440fde7c..2e6ed67f21f 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -17,6 +17,7 @@ export default class MockText { // Phaser.GameObjects.Text.prototype.updateText = () => null; // Phaser.Textures.TextureManager.prototype.addCanvas = () => {}; UI.prototype.showText = this.showText; + UI.prototype.showDialogue = this.showDialogue; // super(scene, x, y); // this.phaserText = new Phaser.GameObjects.Text(scene, x, y, content, styleOptions); } @@ -79,6 +80,13 @@ export default class MockText { } } + showDialogue(text, name, delay, callback, callbackDelay, promptDelay) { + this.scene.messageWrapper.showDialogue(text, name, delay, callback, callbackDelay, promptDelay); + if (callback) { + callback(); + } + } + setScale(scale) { // return this.phaserText.setScale(scale); } diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index dd45d972b50..85d458bf826 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -1,61 +1,90 @@ import { Weather, WeatherType } from "#app/data/weather"; import { Biome } from "#app/enums/biome"; import * as Overrides from "#app/overrides"; -import { vi } from "vitest"; +import { MockInstance, vi } from "vitest"; +import GameManager from "#test/utils/gameManager"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import * as overrides from "#app/overrides"; /** * Helper to handle overrides in tests */ export class OverridesHelper { - constructor() {} + game: GameManager; + constructor(game: GameManager) { + this.game = game; + } /** * Override the encounter chance for a mystery encounter. * @param percentage the encounter chance in % + * @returns spy instance */ - mysteryEncounterChance(percentage: number) { + mysteryEncounterChance(percentage: number): MockInstance { const maxRate: number = 256; // 100% const rate = maxRate * (percentage / 100); - vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate); + const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate); this.log(`Mystery encounter chance set to ${percentage}% (=${rate})!`); + return spy; + } + + /** + * Override the encounter that spawns for the scene + * @param encounterType + * @returns spy instance + */ + mysteryEncounter(encounterType: MysteryEncounterType): MockInstance { + const spy = vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(encounterType); + this.log(`Mystery encounter override set to ${encounterType}!`); + return spy; } /** * Override the starting biome - * @warning The biome will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils) + * @warning Any event listeners that are attached to [NewArenaEvent](events\battle-scene.ts) may need to be handled down the line * @param biome the biome to set */ startingBiome(biome: Biome) { - vi.spyOn(Overrides, "STARTING_BIOME_OVERRIDE", "get").mockReturnValue(biome); + this.game.scene.newArena(biome); this.log(`Starting biome set to ${Biome[biome]} (=${biome})!`); } /** * Override the starting wave (index) * @param wave the wave (index) to set. Classic: `1`-`200` + * @returns spy instance */ - startingWave(wave: number) { - vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave); + startingWave(wave: number): MockInstance { + const spy = vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave); this.log(`Starting wave set to ${wave}!`); + return spy; } /** * Override the weather (type) * @param type weather type to set + * @returns spy instance */ - weather(type: WeatherType) { - vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type); + weather(type: WeatherType): MockInstance { + const spy = vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type); this.log(`Weather set to ${Weather[type]} (=${type})!`); + return spy; } /** * Override the seed - * @warning The seed will not be overridden unless you call `workaround_reInitSceneWithOverrides()` (testUtils) * @param seed the seed to set + * @returns spy instance */ - seed(seed: string) { - vi.spyOn(Overrides, "SEED_OVERRIDE", "get").mockReturnValue(seed); + seed(seed: string): MockInstance { + const spy = vi.spyOn(this.game.scene, "resetSeed").mockImplementation(() => { + this.game.scene.waveSeed = seed; + Phaser.Math.RND.sow([seed]); + this.game.scene.rngCounter = 0; + }); + this.game.scene.resetSeed(); this.log(`Seed set to "${seed}"!`); + return spy; } private log(...params: any[]) { diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index 71cf6b495ed..aa297e4944c 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -108,7 +108,7 @@ export default class PhaseInterceptor { ]; private endBySetMode = [ - TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase, PostMysteryEncounterPhase + TitlePhase, SelectGenderPhase, CommandPhase, SelectModifierPhase, MysteryEncounterPhase, PostMysteryEncounterPhase ]; /** diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index a8461b3a5db..b922fc9c61c 100644 --- a/src/test/utils/testUtils.ts +++ b/src/test/utils/testUtils.ts @@ -1,6 +1,5 @@ import i18next, { type ParseKeys } from "i18next"; import { vi } from "vitest"; -import GameManager from "./gameManager"; /** * Sets up the i18next mock. @@ -22,15 +21,3 @@ export function mockI18next() { export function arrayOfRange(start: integer, end: integer) { return Array.from({ length: end - start }, (_v, k) => k + start); } - -/** - * Woraround to reinitialize the game scene with overrides being set properly. - * By default the scene is initialized without all overrides even having a chance to be applied. - * @warning USE AT YOUR OWN RISK! Might be deleted in the future - * @param game The game manager - * @deprecated - */ -export async function workaround_reInitSceneWithOverrides(game: GameManager) { - await game.runToTitle(); - game.gameWrapper.setScene(game.scene); -} From 1ffefe744d93b9cebefc06c615c40fbce3e50c11 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 11:19:39 -0400 Subject: [PATCH 05/27] attach github issues to all encounter jsdocs --- .../encounters/dark-deal-encounter.ts | 7 ++++++- .../encounters/department-store-sale-encounter.ts | 5 +++++ .../encounters/field-trip-encounter.ts | 5 +++++ .../encounters/fiery-fallout-encounter.ts | 5 +++++ .../encounters/fight-or-flight-encounter.ts | 5 +++++ .../encounters/lost-at-sea-encounter.ts | 2 +- .../encounters/mysterious-challengers-encounter.ts | 5 +++++ .../encounters/mysterious-chest-encounter.ts | 5 +++++ .../encounters/safari-zone-encounter.ts | 5 +++++ .../encounters/shady-vitamin-dealer-encounter.ts | 5 +++++ .../encounters/sleeping-snorlax-encounter.ts | 14 +++++++------- .../encounters/training-session-encounter.ts | 5 +++++ src/locales/en/mystery-encounter.ts | 4 ++-- .../{lost-at-sea.ts => lost-at-sea-dialogue.ts} | 2 +- .../encounters/lost-at-sea-encounter.test.ts | 2 +- 15 files changed, 63 insertions(+), 13 deletions(-) rename src/locales/en/mystery-encounters/{lost-at-sea.ts => lost-at-sea-dialogue.ts} (97%) diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index f9a339db8c2..103fe82ea9f 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -15,7 +15,7 @@ import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data /** i18n namespace for encounter */ const namespace = "mysteryEncounter:dark_deal"; -// Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals +/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */ const excludedBosses = [ Species.NECROZMA, Species.COSMOG, @@ -68,6 +68,11 @@ const excludedBosses = [ Species.PECHARUNT, ]; +/** + * Dark Deal encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/61 | GitHub Issue #61} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const DarkDealEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL) .withEncounterTier(MysteryEncounterTier.ROGUE) diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index f3bb422aa64..4156acdc7b7 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -15,6 +15,11 @@ import IMysteryEncounter, { /** i18n namespace for encounter */ const namespace = "mysteryEncounter:department_store_sale"; +/** + * Department Store Sale encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/33 | GitHub Issue #33} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const DepartmentStoreSaleEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) .withEncounterTier(MysteryEncounterTier.COMMON) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index bc369ee4c4b..2d4aa645f4a 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -21,6 +21,11 @@ import IMysteryEncounter, { /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:field_trip"; +/** + * Field Trip encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/17 | GitHub Issue #17} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const FieldTripEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) .withEncounterTier(MysteryEncounterTier.COMMON) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 45987c928a1..77030cadb04 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -19,6 +19,11 @@ import { randSeedInt } from "#app/utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fiery_fallout"; +/** + * Fiery Fallout encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const FieryFalloutEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.FIERY_FALLOUT diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index db7495f6cfa..8b61b690bfa 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -31,6 +31,11 @@ import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-enco /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fight_or_flight"; +/** + * Fight or Flight encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const FightOrFlightEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.FIGHT_OR_FLIGHT diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 9e25e003e1b..276da15a2ae 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -16,7 +16,7 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY; */ const DAMAGE_PERCENTAGE: number = 25; /** The i18n namespace for the encounter */ -const namepsace = "mysteryEncounter:lostAtSea"; +const namepsace = "mysteryEncounter:lostAtSeaDialogue"; /** * Lost at sea encounter. diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index f9fe8ab3d42..a2df4968f1d 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -23,6 +23,11 @@ import IMysteryEncounter, { /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:mysterious_challengers"; +/** + * Mysterious Challengers encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/41 | GitHub Issue #41} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const MysteriousChallengersEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.MYSTERIOUS_CHALLENGERS diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 9a0a42af2c8..87529900799 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -14,6 +14,11 @@ import IMysteryEncounter, { } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +/** + * Mysterious Chest encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.MYSTERIOUS_CHEST diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 42e4d17d518..18c6b296b42 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -19,6 +19,11 @@ import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter- /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:safari_zone"; +/** + * Safari Zone encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const SafariZoneEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE) .withEncounterTier(MysteryEncounterTier.GREAT) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 7e32ca87c55..5ce90f988bd 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -15,6 +15,11 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:shady_vitamin_dealer"; +/** + * Shady Vitamin Dealer encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/34 | GitHub Issue #34} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const ShadyVitaminDealerEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.SHADY_VITAMIN_DEALER diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts index 9d3659f9443..98013352353 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts @@ -16,11 +16,16 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:sleeping_snorlax"; +/** + * Sleeping Snorlax encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const SleepingSnorlaxEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.SLEEPING_SNORLAX ) - .withEncounterTier(MysteryEncounterTier.ULTRA) + .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 .withCatchAllowed(true) .withHideWildIntroMessage(true) @@ -110,12 +115,7 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = const p = instance.primaryPokemon; p.status = new Status(StatusEffect.SLEEP, 0, 3); p.updateInfo(true); - // const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]); - const sitrus = generateModifierTypeOption( - scene, - modifierTypes.BERRY, - [BerryType.SITRUS] - ); + const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [sitrus], diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 1c0c3633883..0829ce69040 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -21,6 +21,11 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun /** The i18n namespace for the encounter */ const namespace = "mysteryEncounter:training_session"; +/** + * Training Session encounter. + * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/43 | GitHub Issue #43} + * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + */ export const TrainingSessionEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( MysteryEncounterType.TRAINING_SESSION diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index a23518aac60..a35815646b7 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,4 +1,4 @@ -import { lostAtSea } from "./mystery-encounters/lost-at-sea"; +import { lostAtSeaDialogue } from "./mystery-encounters/lost-at-sea-dialogue"; /** * Patterns that can be used: @@ -260,5 +260,5 @@ export const mysteryEncounter = { $@s{item_fanfare}You gained a Berry!`, "sleeping_snorlax_option_3_good_result": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!", - lostAtSea, + lostAtSea: lostAtSeaDialogue, } as const; diff --git a/src/locales/en/mystery-encounters/lost-at-sea.ts b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts similarity index 97% rename from src/locales/en/mystery-encounters/lost-at-sea.ts rename to src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts index 6e37168a255..523dba6e10f 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts @@ -1,4 +1,4 @@ -export const lostAtSea = { +export const lostAtSeaDialogue = { intro: "Wandering aimlessly, you effectively get nowhere.", title: "Lost at sea", description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 14dd43be8bb..48f7275fdff 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -12,7 +12,7 @@ import GameManager from "#app/test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; -const namepsace = "mysteryEncounter:lostAtSea"; +const namepsace = "mysteryEncounter:lostAtSeaDialogue"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; const defaultBiome = Biome.SEA; From a3664b462470e29e496322bf34b1edf1e6303518 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 11:35:57 -0400 Subject: [PATCH 06/27] start dialogue refactor --- .../encounters/mysterious-chest-encounter.ts | 47 +++++++++---------- .../mystery-encounters/dark-deal-dialogue.ts | 31 ++++++++++++ .../department-store-sale-dialogue.ts | 31 ++++++++++++ .../mystery-encounters/field-trip-dialogue.ts | 31 ++++++++++++ .../fiery-fallout-dialogue.ts | 31 ++++++++++++ .../fight-or-flight-dialogue.ts | 31 ++++++++++++ .../lost-at-sea-dialogue.ts | 14 +++--- .../mysterious-challengers-dialogue.ts | 31 ++++++++++++ .../mysterious-chest-dialogue.ts | 27 +++++++++++ .../safari-zone-dialogue.ts | 31 ++++++++++++ .../shady-vitamin-dealer-dialogue.ts | 31 ++++++++++++ .../sleeping-snorlax-dialogue.ts | 31 ++++++++++++ .../training-session-dialogue.ts | 31 ++++++++++++ 13 files changed, 365 insertions(+), 33 deletions(-) create mode 100644 src/locales/en/mystery-encounters/dark-deal-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/department-store-sale-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/field-trip-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/mysterious-chest-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/safari-zone-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts create mode 100644 src/locales/en/mystery-encounters/training-session-dialogue.ts diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 87529900799..91c5aaac0c0 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -1,28 +1,23 @@ import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { - leaveEncounterWithoutBattle, - setEncounterRewards -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { leaveEncounterWithoutBattle, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { randSeedInt } from "#app/utils.js"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; +/** i18n namespace for encounter */ +const namespace = "mysteryEncounter:dark_deal"; + /** * Mysterious Chest encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ export const MysteriousChestEncounter: IMysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.MYSTERIOUS_CHEST - ) + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 180) // waves 2 to 180 .withHideIntroVisuals(false) @@ -39,21 +34,21 @@ export const MysteriousChestEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: "mysteryEncounter:mysterious_chest_intro_message", + text: "${namespace}:intro:message", }, ]) - .withTitle("mysteryEncounter:mysterious_chest_title") - .withDescription("mysteryEncounter:mysterious_chest_description") - .withQuery("mysteryEncounter:mysterious_chest_query") + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label", - buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip", + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: "mysteryEncounter:mysterious_chest_option_1_selected_message", + text: `${namespace}:option:1:selected`, }, ], }) @@ -78,7 +73,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = ], }); // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_normal_result"); + queueEncounterMessage(scene, `${namespace}:option:1:normal`); leaveEncounterWithoutBattle(scene); } else if (roll > 40) { // Choose between 3 ULTRA tier items (20%) @@ -90,7 +85,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = ], }); // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_good_result"); + queueEncounterMessage(scene, `${namespace}:option:1:good`); leaveEncounterWithoutBattle(scene); } else if (roll > 36) { // Choose between 2 ROGUE tier items (4%) @@ -98,7 +93,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE], }); // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_great_result"); + queueEncounterMessage(scene, `${namespace}:option:1:great`); leaveEncounterWithoutBattle(scene); } else if (roll > 35) { // Choose 1 MASTER tier item (1%) @@ -106,7 +101,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = guaranteedModifierTiers: [ModifierTier.MASTER], }); // Display result message then proceed to rewards - queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_amazing_result"); + queueEncounterMessage(scene, `${namespace}:option:1:amazing`); leaveEncounterWithoutBattle(scene); } else { // Your highest level unfainted Pok�mon gets OHKO. Progress with no rewards (35%) @@ -119,7 +114,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name); // Show which Pokemon was KOed, then leave encounter with no rewards // Does this synchronously so that game over doesn't happen over result message - await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result").then(() => { + await showEncounterText(scene, `${namespace}:option:1:bad`).then(() => { leaveEncounterWithoutBattle(scene); }); } @@ -128,11 +123,11 @@ export const MysteriousChestEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label", - buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip", + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: "mysteryEncounter:mysterious_chest_option_2_selected_message", + text: `${namespace}:option:2:selected`, }, ], }, diff --git a/src/locales/en/mystery-encounters/dark-deal-dialogue.ts b/src/locales/en/mystery-encounters/dark-deal-dialogue.ts new file mode 100644 index 00000000000..85a238f7b72 --- /dev/null +++ b/src/locales/en/mystery-encounters/dark-deal-dialogue.ts @@ -0,0 +1,31 @@ +export const darkDealDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts new file mode 100644 index 00000000000..608cb0df5ff --- /dev/null +++ b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts @@ -0,0 +1,31 @@ +export const departmentStoreSaleDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/field-trip-dialogue.ts b/src/locales/en/mystery-encounters/field-trip-dialogue.ts new file mode 100644 index 00000000000..9efa781de95 --- /dev/null +++ b/src/locales/en/mystery-encounters/field-trip-dialogue.ts @@ -0,0 +1,31 @@ +export const fieldTripDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts new file mode 100644 index 00000000000..a9d1a8c3c49 --- /dev/null +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -0,0 +1,31 @@ +export const fieryFalloutDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts b/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts new file mode 100644 index 00000000000..fcacae8ecb2 --- /dev/null +++ b/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts @@ -0,0 +1,31 @@ +export const fightOrFlightDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts index 523dba6e10f..344640610be 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts @@ -1,7 +1,7 @@ export const lostAtSeaDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { @@ -9,16 +9,16 @@ export const lostAtSeaDialogue = { label_disabled: "Can't {{option1RequiredMove}}", tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", diff --git a/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts new file mode 100644 index 00000000000..bdb15b4e7b4 --- /dev/null +++ b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts @@ -0,0 +1,31 @@ +export const mysteriousChallengersDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/mysterious-chest-dialogue.ts b/src/locales/en/mystery-encounters/mysterious-chest-dialogue.ts new file mode 100644 index 00000000000..74a578cfe39 --- /dev/null +++ b/src/locales/en/mystery-encounters/mysterious-chest-dialogue.ts @@ -0,0 +1,27 @@ +export const mysteriousChestDialogue = { + intro: "You found...@d{32} a chest?", + title: "The Mysterious Chest", + description: "A beautifully ornamented chest stands on the ground. There must be something good inside... right?", + query: "Will you open it?", + option: { + 1: { + label: "Open it", + tooltip: "@[SUMMARY_BLUE]{(35%) Something terrible}\n@[SUMMARY_GREEN]{(40%) Okay Rewards}\n@[SUMMARY_GREEN]{(20%) Good Rewards}\n@[SUMMARY_GREEN]{(4%) Great Rewards}\n@[SUMMARY_GREEN]{(1%) Amazing Rewards}", + selected: "You open the chest to find...", + normal: "Just some normal tools and items.", + good: "Some pretty nice tools and items.", + great: "A couple great tools and items!", + amazing: "Whoa! An amazing item!", + bad: `Oh no!@d{32}\nThe chest was trapped! + $Your {{pokeName}} jumps in front of you\nbut is KOed in the process.`, + }, + 2: { + label: "It's too risky, leave", + tooltip: "(-) No Rewards", + selected: "You hurry along your way,\nwith a slight feeling of regret.", + }, + } +}; + + + diff --git a/src/locales/en/mystery-encounters/safari-zone-dialogue.ts b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts new file mode 100644 index 00000000000..34fb6a9b44e --- /dev/null +++ b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts @@ -0,0 +1,31 @@ +export const safariZoneDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts new file mode 100644 index 00000000000..ffa864a2efe --- /dev/null +++ b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts @@ -0,0 +1,31 @@ +export const shadyVitaminDealerDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts b/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts new file mode 100644 index 00000000000..bb93657e8f3 --- /dev/null +++ b/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts @@ -0,0 +1,31 @@ +export const sleepingSnorlaxDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; diff --git a/src/locales/en/mystery-encounters/training-session-dialogue.ts b/src/locales/en/mystery-encounters/training-session-dialogue.ts new file mode 100644 index 00000000000..905141e57ca --- /dev/null +++ b/src/locales/en/mystery-encounters/training-session-dialogue.ts @@ -0,0 +1,31 @@ +export const trainingSessionDialogue = { + intro: "Wandering aimlessly, you effectively get nowhere.", + title: "Lost at sea", + description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + query: "What will you do?", + option: { + 1: { + label: "{{option1PrimaryName}} can help", + label_disabled: "Can't {{option1RequiredMove}}", + tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + selected: + "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 2: { + label: "{{option2PrimaryName}} can help", + label_disabled: "Can't {{option2RequiredMove}}", + tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + selected: + "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + }, + 3: { + label: "Wander aimlessly", + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", + selected: `You float about in the boat, steering it aimlessly until you finally get back on track. + $You and your Pokémon get very fatigued during the whole ordeal.`, + }, + }, + outro: "You are back on track." +}; From fd48c3fcaec8937fe2404063b4bae11b1bc66a50 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 13:41:50 -0400 Subject: [PATCH 07/27] update sleeping snorlax encounter --- src/data/battler-tags.ts | 2 +- .../encounters/dark-deal-encounter.ts | 40 ++++---- .../encounters/fight-or-flight-encounter.ts | 34 +++---- .../encounters/lost-at-sea-encounter.ts | 38 ++++---- .../mysterious-challengers-encounter.ts | 2 +- .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/sleeping-snorlax-encounter.ts | 97 ++++++++----------- .../mystery-encounters/mystery-encounter.ts | 2 +- .../mystery-encounters/mystery-encounters.ts | 13 +-- .../utils/encounter-phase-utils.ts | 67 ++++++++----- src/field/pokemon.ts | 3 +- src/locales/en/mystery-encounter.ts | 92 +++++------------- .../mystery-encounters/dark-deal-dialogue.ts | 44 ++++----- .../department-store-sale-dialogue.ts | 24 ++--- .../mystery-encounters/field-trip-dialogue.ts | 24 ++--- .../fiery-fallout-dialogue.ts | 24 ++--- .../fight-or-flight-dialogue.ts | 40 ++++---- .../lost-at-sea-dialogue.ts | 10 +- .../mysterious-challengers-dialogue.ts | 24 ++--- .../safari-zone-dialogue.ts | 24 ++--- .../shady-vitamin-dealer-dialogue.ts | 24 ++--- .../sleeping-snorlax-dialogue.ts | 24 ++--- .../training-session-dialogue.ts | 24 ++--- src/overrides.ts | 4 +- src/phases.ts | 2 +- ...r-phase.ts => mystery-encounter-phases.ts} | 23 +++++ .../mystery-encounter/encounterTestUtils.ts | 2 +- .../encounters/lost-at-sea-encounter.test.ts | 36 +++---- .../mystery-encounter.test.ts | 2 +- .../phases/mystery-encounter-phase.test.ts | 2 +- src/test/utils/gameManager.ts | 2 +- src/test/utils/phaseInterceptor.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 2 +- 33 files changed, 362 insertions(+), 393 deletions(-) rename src/phases/{mystery-encounter-phase.ts => mystery-encounter-phases.ts} (96%) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index e78977b56ba..060bf3efa19 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1554,7 +1554,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag { const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled); if (!cancelled.value) { - const mysteryEncounterBattleEffects = pokemon.summonData.mysteryEncounterBattleEffects; + const mysteryEncounterBattleEffects = pokemon.mysteryEncounterBattleEffects; if (mysteryEncounterBattleEffects) { mysteryEncounterBattleEffects(pokemon); } diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 103fe82ea9f..23ccba9c32e 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -13,7 +13,7 @@ import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveE import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** i18n namespace for encounter */ -const namespace = "mysteryEncounter:dark_deal"; +const namespace = "mysteryEncounter:darkDeal"; /** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and egg-locked mythicals */ const excludedBosses = [ @@ -91,32 +91,32 @@ export const DarkDealEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, { - speaker: `${namespace}_speaker`, - text: `${namespace}_intro_dialogue`, + speaker: `${namespace}:speaker`, + text: `${namespace}:intro_dialogue`, }, ]) .withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 .withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party .withCatchAllowed(true) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - speaker: `${namespace}_speaker`, - text: `${namespace}_option_1_selected`, + speaker: `${namespace}:speaker`, + text: `${namespace}:option:1:selected_dialogue`, }, { - text: `${namespace}_option_1_selected_message`, + text: `${namespace}:option:1:selected_message`, }, ], }) @@ -126,10 +126,7 @@ export const DarkDealEncounter: IMysteryEncounter = const removedPokemon = getRandomPlayerPokemon(scene, false, true); scene.removePokemonFromPlayerParty(removedPokemon); - scene.currentBattle.mysteryEncounter.setDialogueToken( - "pokeName", - removedPokemon.name - ); + scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.name); // Store removed pokemon types scene.currentBattle.mysteryEncounter.misc = [ @@ -158,7 +155,6 @@ export const DarkDealEncounter: IMysteryEncounter = pokemonConfig.formIndex = 0; } const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.75, pokemonConfigs: [pokemonConfig], }; return initBattleWithEnemyConfig(scene, config); @@ -167,12 +163,12 @@ export const DarkDealEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - speaker: `${namespace}_speaker`, - text: `${namespace}_option_2_selected`, + speaker: `${namespace}:speaker`, + text: `${namespace}:option:2:selected`, }, ], }, @@ -184,7 +180,7 @@ export const DarkDealEncounter: IMysteryEncounter = ) .withOutroDialogue([ { - text: `${namespace}_outro` + text: `${namespace}:outro` } ]) .build(); diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 8b61b690bfa..fc8ba869fe9 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -102,23 +102,23 @@ export const FightOrFlightEncounter: IMysteryEncounter = const primaryPokemon = encounter.options[1].primaryPokemon; if (primaryPokemon) { // Use primaryPokemon to execute the thievery - encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`; + encounter.options[1].dialogue.buttonTooltip = `${namespace}:option:2:tooltip_special`; } else { - encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`; + encounter.options[1].dialogue.buttonTooltip = `${namespace}:option:2:tooltip`; } return true; }) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withSimpleOption( { - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_1_selected_message`, + text: `${namespace}:option:1:selected`, }, ], }, @@ -135,8 +135,8 @@ export const FightOrFlightEncounter: IMysteryEncounter = .withOptionMode(EncounterOptionMode.DEFAULT_OR_SPECIAL) .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withDialogue({ - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, }) .withOptionPhase(async (scene: BattleScene) => { // Pick steal @@ -148,7 +148,7 @@ export const FightOrFlightEncounter: IMysteryEncounter = const primaryPokemon = encounter.options[1].primaryPokemon; if (primaryPokemon) { // Use primaryPokemon to execute the thievery - await showEncounterText(scene, `${namespace}_option_2_steal_result`); + await showEncounterText(scene, `${namespace}:option:2:steal_result`); leaveEncounterWithoutBattle(scene); return; } @@ -160,15 +160,15 @@ export const FightOrFlightEncounter: IMysteryEncounter = config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); - queueEncounterMessage(pokemon.scene, `${namespace}_boss_enraged`); + queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); }; - await showEncounterText(scene, `${namespace}_option_2_bad_result`); + await showEncounterText(scene, `${namespace}:option:2:bad_result`); await initBattleWithEnemyConfig(scene, config); } else { // Steal item (37.5%) // Display result message then proceed to rewards - await showEncounterText(scene, `${namespace}_option_2_good_result`); + await showEncounterText(scene, `${namespace}:option:2:good_result`); leaveEncounterWithoutBattle(scene); } }) @@ -176,11 +176,11 @@ export const FightOrFlightEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, selected: [ { - text: `${namespace}_option_3_selected`, + text: `${namespace}:option:3:selected`, }, ], }, diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 276da15a2ae..fb7d1bcbbb5 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -16,7 +16,7 @@ const OPTION_2_REQUIRED_MOVE = Moves.FLY; */ const DAMAGE_PERCENTAGE: number = 25; /** The i18n namespace for the encounter */ -const namepsace = "mysteryEncounter:lostAtSeaDialogue"; +const namespace = "mysteryEncounter:lostAtSea"; /** * Lost at sea encounter. @@ -35,7 +35,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with y: 3, }, ]) - .withIntroDialogue([{ text: `${namepsace}:intro` }]) + .withIntroDialogue([{ text: `${namespace}:intro` }]) .withOnInit((scene: BattleScene) => { const { mysteryEncounter } = scene.currentBattle; @@ -45,22 +45,22 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with return true; }) - .withTitle(`${namepsace}:title`) - .withDescription(`${namepsace}:description`) - .withQuery(`${namepsace}:query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( // Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/ new MysteryEncounterOptionBuilder() .withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withDialogue({ - buttonLabel: `${namepsace}:option:1:label`, - disabledButtonLabel: `${namepsace}:option:1:label_disabled`, - buttonTooltip: `${namepsace}:option:1:tooltip`, - disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`, + buttonLabel: `${namespace}:option:1:label`, + disabledButtonLabel: `${namespace}:option:1:label_disabled`, + buttonTooltip: `${namespace}:option:1:tooltip`, + disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`, selected: [ { - text: `${namepsace}:option:1:selected`, + text: `${namespace}:option:1:selected`, }, ], }) @@ -73,13 +73,13 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withDialogue({ - buttonLabel: `${namepsace}:option:2:label`, - disabledButtonLabel: `${namepsace}:option:2:label_disabled`, - buttonTooltip: `${namepsace}:option:2:tooltip`, - disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`, + buttonLabel: `${namespace}:option:2:label`, + disabledButtonLabel: `${namespace}:option:2:label_disabled`, + buttonTooltip: `${namespace}:option:2:tooltip`, + disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`, selected: [ { - text: `${namepsace}:option:2:selected`, + text: `${namespace}:option:2:selected`, }, ], }) @@ -89,11 +89,11 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with .withSimpleOption( // Option 3: Wander aimlessly { - buttonLabel: `${namepsace}:option:3:label`, - buttonTooltip: `${namepsace}:option:3:tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, selected: [ { - text: `${namepsace}:option:3:selected`, + text: `${namespace}:option:3:selected`, }, ], }, @@ -113,7 +113,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with ) .withOutroDialogue([ { - text: `${namepsace}:outro`, + text: `${namespace}:outro`, }, ]) .build(); diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index a2df4968f1d..a46f6fd5f8d 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -99,7 +99,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ trainerConfig: brutalConfig, - levelAdditiveMultiplier: 1.1, + levelAdditiveMultiplier: 1, female: female, }); diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 91c5aaac0c0..67ca533e893 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -9,7 +9,7 @@ import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } fro import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; /** i18n namespace for encounter */ -const namespace = "mysteryEncounter:dark_deal"; +const namespace = "mysteryEncounter:mysteriousChest"; /** * Mysterious Chest encounter. diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts index 98013352353..0e60304d9dc 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts @@ -1,17 +1,19 @@ import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { BerryType } from "#enums/berry-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; -import BattleScene from "../../../battle-scene"; -import * as Utils from "../../../utils"; -import { getPokemonSpecies } from "../../pokemon-species"; -import { Status, StatusEffect } from "../../status-effect"; +import BattleScene from "#app/battle-scene"; +import { StatusEffect } from "#app/data/status-effect"; import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoveRequirement } from "../mystery-encounter-requirements"; -import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; +import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { Moves } from "#enums/moves"; +import { BattlerIndex } from "#app/battle"; +import { PokemonMove } from "#app/field/pokemon"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { PartyHealPhase } from "#app/phases"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:sleeping_snorlax"; @@ -54,13 +56,18 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = const pokemonConfig: EnemyPokemonConfig = { species: bossSpecies, isBoss: true, - status: StatusEffect.SLEEP, + status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves + moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT] }; const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 2, + levelAdditiveMultiplier: 0.5, pokemonConfigs: [pokemonConfig], }; encounter.enemyPartyConfigs = [config]; + + // Load animations/sfx for Snorlax fight start moves + initCustomMovesForEncounter(scene, [Moves.SNORE]); + return true; }) .withTitle(`${namespace}_title`) @@ -78,12 +85,22 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = }, async (scene: BattleScene) => { // Pick battle - // TODO: do we want special rewards for this? - // setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); - await initBattleWithEnemyConfig( - scene, - scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0] - ); + const encounter = scene.currentBattle.mysteryEncounter; + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true}); + encounter.startOfBattleEffects.push( + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.PLAYER], + move: new PokemonMove(Moves.SNORE), + ignorePp: true + }, + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.PLAYER], + move: new PokemonMove(Moves.SNORE), + ignorePp: true + }); + await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); } ) .withSimpleOption( @@ -97,46 +114,11 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = ], }, async (scene: BattleScene) => { - const instance = scene.currentBattle.mysteryEncounter; - let roll: integer; - scene.executeWithSeedOffset(() => { - roll = Utils.randSeedInt(16, 0); - }, scene.currentBattle.waveIndex); - - // Half Snorlax exp to entire party - setEncounterExp( - scene, - scene.getParty().map((p) => p.id), - 98 - ); - - if (roll > 4) { - // Fall asleep and get a sitrus berry (75%) - const p = instance.primaryPokemon; - p.status = new Status(StatusEffect.SLEEP, 0, 3); - p.updateInfo(true); - const sitrus = generateModifierTypeOption(scene, modifierTypes.BERRY, [BerryType.SITRUS]); - - setEncounterRewards(scene, { - guaranteedModifierTypeOptions: [sitrus], - fillRemaining: false, - }); - queueEncounterMessage(scene, `${namespace}_option_2_bad_result`); - leaveEncounterWithoutBattle(scene); - } else { - // Heal to full (25%) - for (const pokemon of scene.getParty()) { - pokemon.hp = pokemon.getMaxHp(); - pokemon.resetStatus(); - for (const move of pokemon.moveset) { - move.ppUsed = 0; - } - pokemon.updateInfo(true); - } - - queueEncounterMessage(scene, `${namespace}_option_2_good_result`); - leaveEncounterWithoutBattle(scene); - } + // Fall asleep waiting for Snorlax + // Full heal party + scene.unshiftPhase(new PartyHealPhase(scene, true)); + queueEncounterMessage(scene, `${namespace}_option_2_good_result`); + leaveEncounterWithoutBattle(scene); } ) .withOption( @@ -151,13 +133,10 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { // Steal the Snorlax's Leftovers const instance = scene.currentBattle.mysteryEncounter; - setEncounterRewards(scene, { - guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], - fillRemaining: false, - }); + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false }); queueEncounterMessage(scene, `${namespace}_option_3_good_result`); // Snorlax exp to Pokemon that did the stealing - setEncounterExp(scene, [instance.primaryPokemon.id], 189); + setEncounterExp(scene, instance.primaryPokemon.id, getPokemonSpecies(Species.SNORLAX).baseExp); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 1c6ad219799..5b65cb12260 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -43,8 +43,8 @@ export interface StartOfBattleEffect { sourceBattlerIndex?: BattlerIndex; targets: BattlerIndex[]; move: PokemonMove; + ignorePp: boolean; followUp?: boolean; - ignorePp?: boolean; } export default interface IMysteryEncounter { diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 77e22fce9e9..672fb3300a4 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -158,14 +158,15 @@ const anyBiomeEncounters: MysteryEncounterType[] = [ */ export const mysteryEncountersByBiome = new Map([ [Biome.TOWN, []], - [Biome.PLAINS, []], + [Biome.PLAINS, [ + MysteryEncounterType.SLEEPING_SNORLAX + ]], [Biome.GRASS, [ MysteryEncounterType.SLEEPING_SNORLAX, ]], [Biome.TALL_GRASS, []], [Biome.METROPOLIS, []], [Biome.FOREST, [ - MysteryEncounterType.SLEEPING_SNORLAX, MysteryEncounterType.SAFARI_ZONE ]], @@ -178,13 +179,9 @@ export const mysteryEncountersByBiome = new Map([ [Biome.BEACH, []], [Biome.LAKE, []], [Biome.SEABED, []], - [Biome.MOUNTAIN, [ - MysteryEncounterType.SLEEPING_SNORLAX - ]], + [Biome.MOUNTAIN, []], [Biome.BADLANDS, []], - [Biome.CAVE, [ - MysteryEncounterType.SLEEPING_SNORLAX - ]], + [Biome.CAVE, []], [Biome.DESERT, []], [Biome.ICE_CAVE, []], [Biome.MEADOW, []], diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 6e13bbb0874..44b9f7c8c7e 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -3,13 +3,13 @@ import { biomeLinks } from "#app/data/biomes"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; +import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import * as Overrides from "#app/overrides"; import { BattleEndPhase, EggLapsePhase, ExpPhase, GameOverPhase, ModifierRewardPhase, MovePhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases"; -import { MysteryEncounterBattlePhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import PokemonData from "#app/system/pokemon-data"; import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; @@ -30,25 +30,27 @@ import { Gender } from "#app/data/gender"; import { Moves } from "#enums/moves"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; -export class EnemyPokemonConfig { +export interface EnemyPokemonConfig { species: PokemonSpecies; - isBoss: boolean = false; + isBoss: boolean; bossSegments?: number; bossSegmentModifier?: number; // Additive to the determined segment number formIndex?: number; level?: number; gender?: Gender; - modifierTypes?: PokemonHeldItemModifierType[]; - dataSource?: PokemonData; - tags?: BattlerTagType[]; - mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; - status?: StatusEffect; passive?: boolean; + moveSet?: Moves[]; + /** Can set just the status, or pass a timer on the status turns */ + status?: StatusEffect | [StatusEffect, number]; + mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; + modifierTypes?: PokemonHeldItemModifierType[]; + tags?: BattlerTagType[]; + dataSource?: PokemonData; } -export class EnemyPartyConfig { - levelAdditiveMultiplier?: number = 0; // Formula for enemy: level += waveIndex / 10 * levelAdditive - doubleBattle?: boolean = false; +export interface EnemyPartyConfig { + levelAdditiveMultiplier?: number; // Formula for enemy: level += waveIndex / 10 * levelAdditive + doubleBattle?: boolean; trainerType?: TrainerType; // Generates trainer battle solely off trainer type trainerConfig?: TrainerConfig; // More customizable option for configuring trainer battle pokemonConfigs?: EnemyPokemonConfig[]; @@ -167,11 +169,6 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.formIndex = config.formIndex; } - // Set gender - if (!isNullOrUndefined(config.gender)) { - enemyPokemon.gender = config.gender; - } - // Set Boss if (config.isBoss) { let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); @@ -189,21 +186,40 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set Status if (partyConfig.pokemonConfigs[e].status) { // Default to cureturn 3 for sleep - const cureTurn = partyConfig.pokemonConfigs[e].status === StatusEffect.SLEEP ? 3 : null; - enemyPokemon.status = new Status(partyConfig.pokemonConfigs[e].status, 0, cureTurn); + const status = partyConfig.pokemonConfigs[e].status instanceof Array ? partyConfig.pokemonConfigs[e].status[0] : partyConfig.pokemonConfigs[e].status; + const cureTurn = partyConfig.pokemonConfigs[e].status instanceof Array ? partyConfig.pokemonConfigs[e].status[1] : partyConfig.pokemonConfigs[e].status === StatusEffect.SLEEP ? 3 : null; + enemyPokemon.status = new Status(status, 0, cureTurn); + } + + // Set summon data fields + + // Set gender + if (!isNullOrUndefined(config.gender)) { + enemyPokemon.gender = config.gender; + enemyPokemon.summonData.gender = config.gender; + } + + // Set moves + if (config?.moveSet?.length > 0) { + const moves = config.moveSet.map(m => new PokemonMove(m)); + enemyPokemon.moveset = moves; + enemyPokemon.summonData.moveset = moves; } // Set tags if (config.tags?.length > 0) { const tags = config.tags; tags.forEach(tag => enemyPokemon.addTag(tag)); - // mysteryEncounterBattleEffects can be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied - enemyPokemon.summonData.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects; - - // Requires re-priming summon data so that tags are not cleared on SummonPhase - enemyPokemon.primeSummonData(enemyPokemon.summonData); } + // mysteryEncounterBattleEffects will only be used IFF MYSTERY_ENCOUNTER_POST_SUMMON tag is applied + if (config.mysteryEncounterBattleEffects) { + enemyPokemon.mysteryEncounterBattleEffects = config.mysteryEncounterBattleEffects; + } + + // Requires re-priming summon data to update everything properly + enemyPokemon.primeSummonData(enemyPokemon.summonData); + enemyPokemon.initBattleInfo(); enemyPokemon.getBattleInfo().initInfo(enemyPokemon); } @@ -626,6 +642,9 @@ export function handleEncounterStartOfBattleEffects(scene: BattleScene) { scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.followUp)); }); + // Pseudo turn end phase to reset flinch states, Endure, etc. + scene.pushPhase(new MysteryEncounterBattleStartCleanupPhase(scene)); + encounter.startOfBattleEffectsComplete = true; } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ebae5fe8122..65fd982cbdc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -101,6 +101,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public battleSummonData: PokemonBattleSummonData; public turnData: PokemonTurnData; + public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null; + public fieldPosition: FieldPosition; public maskEnabled: boolean; @@ -3931,7 +3933,6 @@ export class PokemonSummonData { public moveset: PokemonMove[]; // If not initialized this value will not be populated from save data. public types: Type[] = null; - public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null; } export class PokemonBattleData { diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index a35815646b7..3e15782d7f0 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,4 +1,15 @@ import { lostAtSeaDialogue } from "./mystery-encounters/lost-at-sea-dialogue"; +import { mysteriousChestDialogue } from "#app/locales/en/mystery-encounters/mysterious-chest-dialogue"; +import { mysteriousChallengersDialogue } from "#app/locales/en/mystery-encounters/mysterious-challengers-dialogue"; +import { darkDealDialogue } from "#app/locales/en/mystery-encounters/dark-deal-dialogue"; +import { departmentStoreSaleDialogue } from "#app/locales/en/mystery-encounters/department-store-sale-dialogue"; +import { fieldTripDialogue } from "#app/locales/en/mystery-encounters/field-trip-dialogue"; +import { fieryFalloutDialogue } from "#app/locales/en/mystery-encounters/fiery-fallout-dialogue"; +import { fightOrFlightDialogue } from "#app/locales/en/mystery-encounters/fight-or-flight-dialogue"; +import { safariZoneDialogue } from "#app/locales/en/mystery-encounters/safari-zone-dialogue"; +import { shadyVitaminDealerDialogue } from "#app/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue"; +import { sleepingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/sleeping-snorlax-dialogue"; +import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/training-session-dialogue"; /** * Patterns that can be used: @@ -21,47 +32,6 @@ export const mysteryEncounter = { "paid_money": "You paid ₽{{amount, number}}.", "receive_money": "You received ₽{{amount, number}}!", - // Mystery Encounters -- Common Tier - - "mysterious_chest_intro_message": "You found...@d{32} a chest?", - "mysterious_chest_title": "The Mysterious Chest", - "mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?", - "mysterious_chest_query": "Will you open it?", - "mysterious_chest_option_1_label": "Open it", - "mysterious_chest_option_1_tooltip": "@[SUMMARY_BLUE]{(35%) Something terrible}\n@[SUMMARY_GREEN]{(40%) Okay Rewards}\n@[SUMMARY_GREEN]{(20%) Good Rewards}\n@[SUMMARY_GREEN]{(4%) Great Rewards}\n@[SUMMARY_GREEN]{(1%) Amazing Rewards}", - "mysterious_chest_option_2_label": "It's too risky, leave", - "mysterious_chest_option_2_tooltip": "(-) No Rewards", - "mysterious_chest_option_1_selected_message": "You open the chest to find...", - "mysterious_chest_option_2_selected_message": "You hurry along your way,\nwith a slight feeling of regret.", - "mysterious_chest_option_1_normal_result": "Just some normal tools and items.", - "mysterious_chest_option_1_good_result": "Some pretty nice tools and items.", - "mysterious_chest_option_1_great_result": "A couple great tools and items!", - "mysterious_chest_option_1_amazing_result": "Whoa! An amazing item!", - "mysterious_chest_option_1_bad_result": `Oh no!@d{32}\nThe chest was trapped! - $Your {{pokeName}} jumps in front of you\nbut is KOed in the process.`, - - "fight_or_flight_intro_message": "Something shiny is sparkling\non the ground near that Pokémon!", - "fight_or_flight_title": "Fight or Flight", - "fight_or_flight_description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.", - "fight_or_flight_query": "What will you do?", - "fight_or_flight_option_1_label": "Battle the Pokémon", - "fight_or_flight_option_1_tooltip": "(-) Hard Battle\n(+) New Item", - "fight_or_flight_option_2_label": "Steal the item", - "fight_or_flight_option_2_tooltip": "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}", - "fight_or_flight_option_2_steal_tooltip": "(+) {{option2PrimaryName}} uses {{option2PrimaryMove}}", - "fight_or_flight_option_3_label": "Leave", - "fight_or_flight_option_3_tooltip": "(-) No Rewards", - "fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.", - "fight_or_flight_option_2_good_result": `.@d{32}.@d{32}.@d{32} - $You manage to sneak your way\npast and grab the item!`, - "fight_or_flight_option_2_steal_result": `.@d{32}.@d{32}.@d{32} - $Your {{option2PrimaryName}} helps you out and uses {{option2PrimaryMove}}! - $ You nabbed the item!`, - "fight_or_flight_option_2_bad_result": `.@d{32}.@d{32}.@d{32} - $The Pokémon catches you\nas you try to sneak around!`, - "fight_or_flight_boss_enraged": "The opposing {{enemyPokemon}} has become enraged!", - "fight_or_flight_option_3_selected": "You leave the strong Pokémon\nwith its prize and continue on.", - "department_store_sale_intro_message": "It's a lady with a ton of shopping bags.", "department_store_sale_speaker": "Shopper", "department_store_sale_intro_dialogue": `Hello! Are you here for\nthe amazing sales too? @@ -217,28 +187,6 @@ export const mysteryEncounter = { // Mystery Encounters -- Rogue Tier - "dark_deal_intro_message": "A strange man in a tattered coat\nstands in your way...", - "dark_deal_speaker": "Shady Guy", - "dark_deal_intro_dialogue": `Hey, you! - $I've been working on a new device\nto bring out a Pokémon's latent power! - $It completely rebinds the Pokémon's atoms\nat a molecular level into a far more powerful form. - $Hehe...@d{64} I just need some sac-@d{32}\nErr, test subjects, to prove it works.`, - "dark_deal_title": "Dark Deal", - "dark_deal_description": "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"", - "dark_deal_query": "What will you do?", - "dark_deal_option_1_label": "Accept", - "dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", - "dark_deal_option_2_label": "Refuse", - "dark_deal_option_2_tooltip": "(-) No Rewards", - "dark_deal_option_1_selected": `Let's see, that {{pokeName}} will do nicely! - $Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`, - "dark_deal_option_1_selected_message": `The man hands you 5 Rogue Balls. - \${{pokeName}} hops into the strange machine... - $Flashing lights and weird noises\nstart coming from the machine! - $...@d{96} Something emerges\nfrom the device, raging wildly!`, - "dark_deal_option_2_selected": "Not gonna help a poor fellow out?\nPah!", - "dark_deal_outro": "After the harrowing encounter,\nyou collect yourself and depart.", - "sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path. $You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`, "sleeping_snorlax_title": "Sleeping Snorlax", @@ -253,12 +201,20 @@ export const mysteryEncounter = { "sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this", "sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.", "sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32} - $You wait for a time, but the Snorlax's yawns make your party sleepy.`, - "sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found - but your Pokémon are all healed!", - "sleeping_snorlax_option_2_bad_result": `Your {{primaryName}} is still asleep... - $But on the bright side, the Snorlax left something behind... - $@s{item_fanfare}You gained a Berry!`, + $You wait for a time, but the Snorlax's yawns make your party sleepy...`, + "sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!", "sleeping_snorlax_option_3_good_result": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!", + mysteriousChallengers: mysteriousChallengersDialogue, + mysteriousChest: mysteriousChestDialogue, + darkDeal: darkDealDialogue, + fightOrFlight: fightOrFlightDialogue, + sleepingSnorlax: sleepingSnorlaxDialogue, + trainingSession: trainingSessionDialogue, + departmentStoreSale: departmentStoreSaleDialogue, + shadyVitaminDealer: shadyVitaminDealerDialogue, + fieldTrip: fieldTripDialogue, + safariZone: safariZoneDialogue, lostAtSea: lostAtSeaDialogue, + fieryFallout: fieryFalloutDialogue, } as const; diff --git a/src/locales/en/mystery-encounters/dark-deal-dialogue.ts b/src/locales/en/mystery-encounters/dark-deal-dialogue.ts index 85a238f7b72..113209bf61a 100644 --- a/src/locales/en/mystery-encounters/dark-deal-dialogue.ts +++ b/src/locales/en/mystery-encounters/dark-deal-dialogue.ts @@ -1,31 +1,29 @@ export const darkDealDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "A strange man in a tattered coat\nstands in your way...", + speaker: "Shady Guy", + intro_dialogue: `Hey, you! + $I've been working on a new device\nto bring out a Pokémon's latent power! + $It completely rebinds the Pokémon's atoms\nat a molecular level into a far more powerful form. + $Hehe...@d{64} I just need some sac-@d{32}\nErr, test subjects, to prove it works.`, + title: "Dark Deal", + description: "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"", query: "What will you do?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + label: "Accept", + tooltip: "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", + selected_dialogue: `Let's see, that {{pokeName}} will do nicely! + $Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`, + selected_message: `The man hands you 5 Rogue Balls. + \${{pokeName}} hops into the strange machine... + $Flashing lights and weird noises\nstart coming from the machine! + $...@d{96} Something emerges\nfrom the device, raging wildly!` }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", - }, - 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, - }, + label: "Refuse", + tooltip: "(-) No Rewards", + selected: "Not gonna help a poor fellow out?\nPah!", + } }, - outro: "You are back on track." + outro: "After the harrowing encounter,\nyou collect yourself and depart." }; diff --git a/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts index 608cb0df5ff..287e3c9bae2 100644 --- a/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts +++ b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts @@ -1,30 +1,30 @@ export const departmentStoreSaleDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/field-trip-dialogue.ts b/src/locales/en/mystery-encounters/field-trip-dialogue.ts index 9efa781de95..7e4a4041b92 100644 --- a/src/locales/en/mystery-encounters/field-trip-dialogue.ts +++ b/src/locales/en/mystery-encounters/field-trip-dialogue.ts @@ -1,30 +1,30 @@ export const fieldTripDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts index a9d1a8c3c49..2c0549a3374 100644 --- a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -1,30 +1,30 @@ export const fieryFalloutDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts b/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts index fcacae8ecb2..2ef9d31a821 100644 --- a/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts +++ b/src/locales/en/mystery-encounters/fight-or-flight-dialogue.ts @@ -1,31 +1,31 @@ export const fightOrFlightDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Something shiny is sparkling\non the ground near that Pokémon!", + title: "Fight or Flight", + description: "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.", query: "What will you do?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + label: "Battle the Pokémon", + tooltip: "(-) Hard Battle\n(+) New Item", + selected: "You approach the\nPokémon without fear.", }, 2: { label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + tooltip: "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}", + tooltip_special: "(+) {{option2PrimaryName}} uses {{option2PrimaryMove}}", + good_result: `.@d{32}.@d{32}.@d{32} + $You manage to sneak your way\npast and grab the item!`, + special_result: `.@d{32}.@d{32}.@d{32} + $Your {{option2PrimaryName}} helps you out and uses {{option2PrimaryMove}}! + $ You nabbed the item!`, + bad_result: `.@d{32}.@d{32}.@d{32} + $The Pokémon catches you\nas you try to sneak around!`, + boss_enraged: "The opposing {{enemyPokemon}} has become enraged!" }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + label: "Leave", + tooltip: "(-) No Rewards", + selected: "You leave the strong Pokémon\nwith its prize and continue on.", }, - }, - outro: "You are back on track." + } }; diff --git a/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts index 344640610be..cb1de1315cd 100644 --- a/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts +++ b/src/locales/en/mystery-encounters/lost-at-sea-dialogue.ts @@ -7,7 +7,7 @@ export const lostAtSeaDialogue = { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, @@ -15,16 +15,16 @@ export const lostAtSeaDialogue = { 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts index bdb15b4e7b4..20be45af4c9 100644 --- a/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts +++ b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts @@ -1,30 +1,30 @@ export const mysteriousChallengersDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/safari-zone-dialogue.ts b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts index 34fb6a9b44e..46952b7b197 100644 --- a/src/locales/en/mystery-encounters/safari-zone-dialogue.ts +++ b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts @@ -1,30 +1,30 @@ export const safariZoneDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts index ffa864a2efe..9bfdc139ea1 100644 --- a/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts +++ b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts @@ -1,30 +1,30 @@ export const shadyVitaminDealerDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts b/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts index bb93657e8f3..bc2dc65e572 100644 --- a/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts +++ b/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts @@ -1,30 +1,30 @@ export const sleepingSnorlaxDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/locales/en/mystery-encounters/training-session-dialogue.ts b/src/locales/en/mystery-encounters/training-session-dialogue.ts index 905141e57ca..a54adf92492 100644 --- a/src/locales/en/mystery-encounters/training-session-dialogue.ts +++ b/src/locales/en/mystery-encounters/training-session-dialogue.ts @@ -1,30 +1,30 @@ export const trainingSessionDialogue = { - intro: "Wandering aimlessly, you effectively get nowhere.", - title: "Lost at sea", - description: "The sea is turbulent in this area, and you seem to be running out of fuel.\nThis is bad. Is there a way out of the situation?", + intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", + title: "Lost at Sea", + description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", query: "What will you do?", option: { 1: { label: "{{option1PrimaryName}} can help", label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you.\n(+) {{option1PrimaryName}} gains some EXP.", + tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: - "{{option1PrimaryName}} swims ahead, guiding you back on track.\n{{option1PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. + \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { label: "{{option2PrimaryName}} can help", label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you.\n(+) {{option2PrimaryName}} gains some EXP.", + tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: - "{{option2PrimaryName}} flies ahead of your boat, guiding you back on track.\n{{option2PrimaryName}} seems to also have gotten stronger in this time of need.", + selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. + \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP.", - selected: `You float about in the boat, steering it aimlessly until you finally get back on track. - $You and your Pokémon get very fatigued during the whole ordeal.`, + tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. + $You and your Pokémon are fatigued from the whole ordeal.`, }, }, outro: "You are back on track." diff --git a/src/overrides.ts b/src/overrides.ts index e19a5bf20dd..7272f68bcb4 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.SLEEPING_SNORLAX; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/phases.ts b/src/phases.ts index 015a97ca115..5e4bdf29943 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -66,7 +66,7 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phases.ts similarity index 96% rename from src/phases/mystery-encounter-phase.ts rename to src/phases/mystery-encounter-phases.ts index b0acdd3c1e1..6f474500838 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -14,6 +14,7 @@ import { IvScannerModifier } from "../modifier/modifier"; import * as Utils from "../utils"; import { isNullOrUndefined } from "../utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { BattlerTagLapseType } from "#app/data/battler-tags"; /** * Will handle (in order): @@ -165,6 +166,28 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { } } +/** + * Runs at the beginning of an Encounter's battle + * Will cleanup any residual flinches, Endure, etc. that are left over from startOfBattleEffects + * See [TurnEndPhase](../phases.ts) for more details + */ +export class MysteryEncounterBattleStartCleanupPhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + const field = this.scene.getField(true).filter(p => p.summonData); + field.forEach(pokemon => { + pokemon.lapseTags(BattlerTagLapseType.TURN_END); + }); + + this.end(); + } +} + /** * Will handle (in order): * - Setting BGM diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 1a602b7b239..6caff2a8693 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,6 +1,6 @@ import { Button } from "#app/enums/buttons"; import { MessagePhase, VictoryPhase } from "#app/phases"; -import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 48f7275fdff..76a7dca8cdf 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -12,7 +12,7 @@ import GameManager from "#app/test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; -const namepsace = "mysteryEncounter:lostAtSeaDialogue"; +const namespace = "mysteryEncounter:lostAtSeaDialogue"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; const defaultBiome = Biome.SEA; @@ -49,10 +49,10 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); expect(LostAtSeaEncounter.dialogue).toBeDefined(); - expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namepsace}:intro` }]); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namepsace}:title`); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namepsace}:description`); - expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namepsace}:query`); + expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`); + expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`); expect(LostAtSeaEncounter.options.length).toBe(3); }); @@ -100,13 +100,13 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); expect(option1.dialogue).toBeDefined(); expect(option1.dialogue).toStrictEqual({ - buttonLabel: `${namepsace}:option:1:label`, - disabledButtonLabel: `${namepsace}:option:1:label_disabled`, - buttonTooltip: `${namepsace}:option:1:tooltip`, - disabledButtonTooltip: `${namepsace}:option:1:tooltip_disabled`, + buttonLabel: `${namespace}:option:1:label`, + disabledButtonLabel: `${namespace}:option:1:label_disabled`, + buttonTooltip: `${namespace}:option:1:tooltip`, + disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`, selected: [ { - text: `${namepsace}:option:1:selected`, + text: `${namespace}:option:1:selected`, }, ], }); @@ -147,13 +147,13 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); expect(option2.dialogue).toBeDefined(); expect(option2.dialogue).toStrictEqual({ - buttonLabel: `${namepsace}:option:2:label`, - disabledButtonLabel: `${namepsace}:option:2:label_disabled`, - buttonTooltip: `${namepsace}:option:2:tooltip`, - disabledButtonTooltip: `${namepsace}:option:2:tooltip_disabled`, + buttonLabel: `${namespace}:option:2:label`, + disabledButtonLabel: `${namespace}:option:2:label_disabled`, + buttonTooltip: `${namespace}:option:2:tooltip`, + disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`, selected: [ { - text: `${namepsace}:option:2:selected`, + text: `${namespace}:option:2:selected`, }, ], }); @@ -197,11 +197,11 @@ describe("Lost at Sea - Mystery Encounter", () => { expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT); expect(option3.dialogue).toBeDefined(); expect(option3.dialogue).toStrictEqual({ - buttonLabel: `${namepsace}:option:3:label`, - buttonTooltip: `${namepsace}:option:3:tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, selected: [ { - text: `${namepsace}:option:3:selected`, + text: `${namespace}:option:3:selected`, }, ], }); diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index 8ed2e4756ca..b840302278e 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeAll, beforeEach, expect, describe, it } from "vitest"; import GameManager from "#app/test/utils/gameManager"; import Phaser from "phaser"; import { Species } from "#enums/species"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; describe("Mystery Encounters", () => { diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 91e6a09bb99..9fd989e82b8 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -2,7 +2,7 @@ import {afterEach, beforeAll, beforeEach, expect, describe, it, vi } from "vites import GameManager from "#app/test/utils/gameManager"; import Phaser from "phaser"; import {Species} from "#enums/species"; -import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase"; +import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import {Mode} from "#app/ui/ui"; import {Button} from "#enums/buttons"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index f0c7d08c6b7..1b8c5c383a0 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -34,7 +34,7 @@ import { Button } from "#enums/buttons"; import { BattlerIndex } from "#app/battle.js"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler"; -import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase"; +import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phases"; import { OverridesHelper } from "./overridesHelper"; /** diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index aa297e4944c..39278c673d4 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -44,7 +44,7 @@ import { MysteryEncounterPhase, MysteryEncounterRewardsPhase, PostMysteryEncounterPhase -} from "#app/phases/mystery-encounter-phase"; +} from "#app/phases/mystery-encounter-phases"; export default class PhaseInterceptor { public scene; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index e6699afbfe8..cfb2dfbd3a8 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -4,7 +4,7 @@ import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { Button } from "#enums/buttons"; import { addWindow, WindowVariant } from "./ui-theme"; -import { MysteryEncounterPhase } from "../phases/mystery-encounter-phase"; +import { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; import { PartyUiMode } from "./party-ui-handler"; import MysteryEncounterOption, { EncounterOptionMode } from "../data/mystery-encounters/mystery-encounter-option"; import * as Utils from "../utils"; From 872542080a757a09cdab9194747814ff1177db4a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 15:52:43 -0400 Subject: [PATCH 08/27] migrate encounters dialogue to new format --- .../department-store-sale-encounter.ts | 35 ++-- .../encounters/field-trip-encounter.ts | 62 +++--- .../encounters/fiery-fallout-encounter.ts | 29 +-- .../encounters/fight-or-flight-encounter.ts | 5 +- .../mysterious-challengers-encounter.ts | 30 +-- .../encounters/safari-zone-encounter.ts | 65 ++++--- .../shady-vitamin-dealer-encounter.ts | 49 +++-- ...ter.ts => slumbering-snorlax-encounter.ts} | 0 .../encounters/training-session-encounter.ts | 38 ++-- .../mystery-encounter-dialogue.ts | 12 +- .../mystery-encounters/mystery-encounters.ts | 8 +- src/enums/mystery-encounter-type.ts | 2 +- src/locales/en/mystery-encounter.ts | 177 +----------------- .../department-store-sale-dialogue.ts | 40 ++-- .../mystery-encounters/field-trip-dialogue.ts | 44 ++--- .../fiery-fallout-dialogue.ts | 39 ++-- .../mysterious-challengers-dialogue.ts | 33 ++-- .../safari-zone-dialogue.ts | 61 +++--- .../shady-vitamin-dealer-dialogue.ts | 39 ++-- ...ogue.ts => slumbering-snorlax-dialogue.ts} | 0 .../training-session-dialogue.ts | 39 ++-- src/overrides.ts | 4 +- .../encounters/lost-at-sea-encounter.test.ts | 5 +- 23 files changed, 331 insertions(+), 485 deletions(-) rename src/data/mystery-encounters/encounters/{sleeping-snorlax-encounter.ts => slumbering-snorlax-encounter.ts} (100%) rename src/locales/en/mystery-encounters/{sleeping-snorlax-dialogue.ts => slumbering-snorlax-dialogue.ts} (100%) diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 4156acdc7b7..ae025a909b1 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -13,7 +13,7 @@ import IMysteryEncounter, { } from "../mystery-encounter"; /** i18n namespace for encounter */ -const namespace = "mysteryEncounter:department_store_sale"; +const namespace = "mysteryEncounter:departmentStoreSale"; /** * Department Store Sale encounter. @@ -41,21 +41,21 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, { - text: `${namespace}_intro_dialogue`, - speaker: `${namespace}_speaker`, + text: `${namespace}:intro_dialogue`, + speaker: `${namespace}:speaker`, }, ]) .withHideIntroVisuals(false) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withSimpleOption( { - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, }, async (scene: BattleScene) => { // Choose TMs @@ -80,8 +80,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, }, async (scene: BattleScene) => { // Choose Vitamins @@ -104,8 +104,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, }, async (scene: BattleScene) => { // Choose X Items @@ -128,8 +128,8 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_4_label`, - buttonTooltip: `${namespace}_option_4_tooltip`, + buttonLabel: `${namespace}:option:4:label`, + buttonTooltip: `${namespace}:option:4:tooltip`, }, async (scene: BattleScene) => { // Choose Pokeballs @@ -154,4 +154,9 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = leaveEncounterWithoutBattle(scene); } ) + .withOutroDialogue([ + { + text: `${namespace}:outro`, + } + ]) .build(); diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 2d4aa645f4a..da0016db360 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -19,7 +19,7 @@ import IMysteryEncounter, { } from "../mystery-encounter"; /** i18n namespace for the encounter */ -const namespace = "mysteryEncounter:field_trip"; +const namespace = "mysteryEncounter:fieldTrip"; /** * Field Trip encounter. @@ -49,27 +49,27 @@ export const FieldTripEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, { - text: `${namespace}_intro_dialogue`, - speaker: `${namespace}_speaker`, + text: `${namespace}:intro_dialogue`, + speaker: `${namespace}:speaker`, }, ]) .withHideIntroVisuals(false) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, - secondOptionPrompt: `${namespace}_second_option_prompt`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, + secondOptionPrompt: `${namespace}:second_option_prompt`, selected: [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ], }) @@ -87,11 +87,11 @@ export const FieldTripEncounter: IMysteryEncounter = if (!correctMove) { encounter.options[0].dialogue.selected = [ { - text: `${namespace}_option_incorrect`, - speaker: `${namespace}_speaker`, + text: `${namespace}:incorrect`, + speaker: `${namespace}:speaker`, }, { - text: `${namespace}_lesson_learned`, + text: `${namespace}:lesson_learned`, }, ]; setEncounterExp(scene, scene.getParty().map((p) => p.id), 50); @@ -100,7 +100,7 @@ export const FieldTripEncounter: IMysteryEncounter = encounter.setDialogueToken("move", move.getName()); encounter.options[0].dialogue.selected = [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ]; setEncounterExp(scene, [pokemon.id], 100); @@ -138,12 +138,12 @@ export const FieldTripEncounter: IMysteryEncounter = new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, - secondOptionPrompt: `${namespace}_second_option_prompt`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, + secondOptionPrompt: `${namespace}:second_option_prompt`, selected: [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ], }) @@ -161,11 +161,11 @@ export const FieldTripEncounter: IMysteryEncounter = if (!correctMove) { encounter.options[1].dialogue.selected = [ { - text: `${namespace}_option_incorrect`, - speaker: `${namespace}_speaker`, + text: `${namespace}:incorrect`, + speaker: `${namespace}:speaker`, }, { - text: `${namespace}_lesson_learned`, + text: `${namespace}:lesson_learned`, }, ]; setEncounterExp(scene, scene.getParty().map((p) => p.id), 50); @@ -174,7 +174,7 @@ export const FieldTripEncounter: IMysteryEncounter = encounter.setDialogueToken("move", move.getName()); encounter.options[1].dialogue.selected = [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ]; setEncounterExp(scene, [pokemon.id], 100); @@ -212,12 +212,12 @@ export const FieldTripEncounter: IMysteryEncounter = new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, - secondOptionPrompt: `${namespace}_second_option_prompt`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + secondOptionPrompt: `${namespace}:second_option_prompt`, selected: [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ], }) @@ -235,11 +235,11 @@ export const FieldTripEncounter: IMysteryEncounter = if (!correctMove) { encounter.options[2].dialogue.selected = [ { - text: `${namespace}_option_incorrect`, - speaker: `${namespace}_speaker`, + text: `${namespace}:incorrect`, + speaker: `${namespace}:speaker`, }, { - text: `${namespace}_lesson_learned`, + text: `${namespace}:lesson_learned`, }, ]; setEncounterExp( @@ -252,7 +252,7 @@ export const FieldTripEncounter: IMysteryEncounter = encounter.setDialogueToken("move", move.getName()); encounter.options[2].dialogue.selected = [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ]; setEncounterExp(scene, [pokemon.id], 100); diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 77030cadb04..f4c9db5e4f8 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -17,7 +17,7 @@ import { WeatherType } from "#app/data/weather"; import { randSeedInt } from "#app/utils"; /** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:fiery_fallout"; +const namespace = "mysteryEncounter:fieryFallout"; /** * Fiery Fallout encounter. @@ -35,7 +35,7 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) .withOnInit((scene: BattleScene) => { @@ -85,16 +85,16 @@ export const FieryFalloutEncounter: IMysteryEncounter = return true; }) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withSimpleOption( { - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_1_selected`, + text: `${namespace}:option:1:selected`, }, ], }, @@ -133,11 +133,11 @@ export const FieryFalloutEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: `${namespace}_option_2_selected`, + text: `${namespace}:option:2:selected`, }, ], }, @@ -152,11 +152,12 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically .withDialogue({ - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`, selected: [ { - text: `${namespace}_option_3_selected`, + text: `${namespace}:option:3:selected`, }, ], }) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index fc8ba869fe9..ca64ba30552 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -27,9 +27,10 @@ import IMysteryEncounter, { } from "../mystery-encounter"; import { MoveRequirement } from "../mystery-encounter-requirements"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { getPokemonNameWithAffix } from "#app/messages"; /** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:fight_or_flight"; +const namespace = "mysteryEncounter:fightOrFlight"; /** * Fight or Flight encounter. @@ -159,7 +160,7 @@ export const FightOrFlightEncounter: IMysteryEncounter = const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]; config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { - pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name); + pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(pokemon)); queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); }; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index a46f6fd5f8d..b1b61434511 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -21,7 +21,7 @@ import IMysteryEncounter, { } from "../mystery-encounter"; /** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:mysterious_challengers"; +const namespace = "mysteryEncounter:mysteriousChallengers"; /** * Mysterious Challengers encounter. @@ -37,7 +37,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = .withIntroSpriteConfigs([]) // These are set in onInit() .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) .withOnInit((scene: BattleScene) => { @@ -126,16 +126,16 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = return true; }) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withSimpleOption( { - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }, @@ -156,11 +156,11 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }, @@ -181,11 +181,11 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }, @@ -209,7 +209,7 @@ export const MysteriousChallengersEncounter: IMysteryEncounter = ) .withOutroDialogue([ { - text: `${namespace}_outro_win`, + text: `${namespace}:outro`, }, ]) .build(); diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 18c6b296b42..ee0a161408a 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -5,7 +5,6 @@ import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, Myste import MysteryEncounterOption, { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { TrainerSlot } from "#app/data/trainer-config"; import { ScanIvsPhase, SummonPhase, VictoryPhase } from "#app/phases"; -import i18next from "i18next"; import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier"; import { EnemyPokemon } from "#app/field/pokemon"; import { PokeballType } from "#app/data/pokeball"; @@ -14,10 +13,11 @@ import { IntegerHolder, randSeedInt } from "#app/utils"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterTier, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { getPokemonNameWithAffix } from "#app/messages"; /** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:safari_zone"; +const namespace = "mysteryEncounter:safariZone"; /** * Safari Zone encounter. @@ -41,21 +41,21 @@ export const SafariZoneEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption(new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withSceneRequirement(new MoneyRequirement(0, 2.75)) // Cost equal to 1 Max Revive .withDialogue({ - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_1_selected_message`, + text: `${namespace}:option:1:selected`, }, ], }) @@ -67,6 +67,7 @@ export const SafariZoneEncounter: IMysteryEncounter = safariPokemonRemaining: 3 }; updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney); + // Load bait/mud assets scene.loadSe("PRSFX- Bug Bite", "battle_anims"); scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims"); scene.loadSe("PRSFX- Taunt2", "battle_anims"); @@ -80,11 +81,11 @@ export const SafariZoneEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: `${namespace}_option_2_selected_message`, + text: `${namespace}:option:2:selected`, }, ], }, @@ -115,11 +116,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_pokeball_option_label`, - buttonTooltip: `${namespace}_pokeball_option_tooltip`, + buttonLabel: `${namespace}:safari:1:label`, + buttonTooltip: `${namespace}:safari:1:tooltip`, selected: [ { - text: `${namespace}_pokeball_option_selected`, + text: `${namespace}:safari:1:selected`, } ], }) @@ -149,11 +150,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_bait_option_label`, - buttonTooltip: `${namespace}_bait_option_tooltip`, + buttonLabel: `${namespace}:safari:2:label`, + buttonTooltip: `${namespace}:safari:2:tooltip`, selected: [ { - text: `${namespace}_bait_option_selected`, + text: `${namespace}:safari:2:selected`, }, ], }) @@ -167,9 +168,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ // 80% chance to increase flee stage +1 const fleeChangeResult = tryChangeFleeStage(scene, 1, 8); if (!fleeChangeResult) { - await showEncounterText(scene, i18next.t(`${namespace}_pokemon_busy_eating`, { pokemonName: pokemon.name }), 1500, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:busy_eating`), 1000, false ); } else { - await showEncounterText(scene, i18next.t(`${namespace}_pokemon_eating`, { pokemonName: pokemon.name }), 1500, false); + await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:eating`), 1000, false); } await doEndTurn(scene, 1); @@ -179,11 +180,11 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_mud_option_label`, - buttonTooltip: `${namespace}_mud_option_tooltip`, + buttonLabel: `${namespace}:safari:3:label`, + buttonTooltip: `${namespace}:safari:3:tooltip`, selected: [ { - text: `${namespace}_mud_option_selected`, + text: `${namespace}:safari:3:selected`, }, ], }) @@ -196,9 +197,9 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ // 80% chance to decrease catch stage -1 const catchChangeResult = tryChangeCatchStage(scene, -1, 8); if (!catchChangeResult) { - await showEncounterText(scene, i18next.t(`${namespace}_pokemon_beside_itself_angry`, { pokemonName: pokemon.name }), 1500, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:beside_itself_angry`), 1000, false ); } else { - await showEncounterText(scene, i18next.t(`${namespace}_pokemon_angry`, { pokemonName: pokemon.name }), 1500, false ); + await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari:angry`), 1000, false ); } await doEndTurn(scene, 2); @@ -208,8 +209,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_flee_option_label`, - buttonTooltip: `${namespace}_flee_option_tooltip`, + buttonLabel: `${namespace}:safari:4:label`, + buttonTooltip: `${namespace}:safari:4:tooltip`, }) .withOptionPhase(async (scene: BattleScene) => { // Flee option @@ -231,7 +232,8 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [ async function summonSafariPokemon(scene: BattleScene) { const encounter = scene.currentBattle.mysteryEncounter; // Message pokemon remaining - scene.queueMessage(i18next.t(`${namespace}_remaining_count`, { remainingCount: encounter.misc.safariPokemonRemaining}), null, true); + encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining); + scene.queueMessage(getEncounterText(scene, `${namespace}:safari:remaining_count`), null, true); // 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 @@ -279,7 +281,8 @@ async function summonSafariPokemon(scene: BattleScene) { scene.unshiftPhase(new SummonPhase(scene, 0, false)); - showEncounterText(scene, i18next.t("battle:singleWildAppeared", { pokemonName: pokemon.name }), 1500, false) + encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); + showEncounterText(scene, getEncounterText(scene, "battle:singleWildAppeared"), 1500, false) .then(() => { const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { @@ -484,7 +487,7 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number) { leaveEncounterWithoutBattle(scene, true); } } else { - scene.queueMessage(i18next.t(`${namespace}_pokemon_watching`, { pokemonName: pokemon.name }), 0, null, 1000); + scene.queueMessage(getEncounterText(scene, `${namespace}:safari:watching`), 0, null, 1000); initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true }); } } diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 5ce90f988bd..082eb9a1872 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -5,15 +5,14 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { randSeedInt } from "#app/utils"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; -import i18next from "i18next"; import BattleScene from "../../../battle-scene"; import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; -import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; /** the i18n namespace for this encounter */ -const namespace = "mysteryEncounter:shady_vitamin_dealer"; +const namespace = "mysteryEncounter:shadyVitaminDealer"; /** * Shady Vitamin Dealer encounter. @@ -49,26 +48,26 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, { - text: `${namespace}_intro_dialogue`, - speaker: `${namespace}_speaker`, + text: `${namespace}:intro_dialogue`, + speaker: `${namespace}:speaker`, }, ]) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2 .withDialogue({ - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ], }) @@ -95,7 +94,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return i18next.t(`${namespace}_invalid_selection`); + return getEncounterText(scene, `${namespace}:invalid_selection`); } return null; @@ -130,13 +129,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = if (randSeedInt(10) < 8) { if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) { // Toxic applied - queueEncounterMessage(scene, `${namespace}_bad_poison`); + queueEncounterMessage(scene, `${namespace}:bad_poison`); } else { // Pokemon immune or something else prevents status - queueEncounterMessage(scene, `${namespace}_damage_only`); + queueEncounterMessage(scene, `${namespace}:damage_only`); } } else { - queueEncounterMessage(scene, `${namespace}_damage_only`); + queueEncounterMessage(scene, `${namespace}:damage_only`); } setEncounterExp(scene, [chosenPokemon.id], 100); @@ -150,11 +149,11 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5 .withDialogue({ - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: `${namespace}_option_selected`, + text: `${namespace}:option:selected`, }, ], }) @@ -181,7 +180,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = // If pokemon meets primary pokemon reqs, it can be selected const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon); if (!meetsReqs) { - return i18next.t(`${namespace}_invalid_selection`); + return getEncounterText(scene, `${namespace}:invalid_selection`); } return null; @@ -212,13 +211,13 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = if (randSeedInt(10) < 2) { if (chosenPokemon.trySetStatus(StatusEffect.POISON)) { // Poison applied - queueEncounterMessage(scene, `${namespace}_poison`); + queueEncounterMessage(scene, `${namespace}:poison`); } else { // Pokemon immune or something else prevents status - queueEncounterMessage(scene, `${namespace}_no_bad_effects`); + queueEncounterMessage(scene, `${namespace}:no_bad_effects`); } } else { - queueEncounterMessage(scene, `${namespace}_no_bad_effects`); + queueEncounterMessage(scene, `${namespace}:no_bad_effects`); } setEncounterExp(scene, [chosenPokemon.id], 100); @@ -229,8 +228,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, }, async (scene: BattleScene) => { // Leave encounter with no rewards or exp diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts similarity index 100% rename from src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts rename to src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 0829ce69040..e417bcc7ef4 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -19,7 +19,7 @@ import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-e import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; /** The i18n namespace for the encounter */ -const namespace = "mysteryEncounter:training_session"; +const namespace = "mysteryEncounter:trainingSession"; /** * Training Session encounter. @@ -45,21 +45,21 @@ export const TrainingSessionEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }) @@ -170,7 +170,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = scene.addModifier(mod, true, false, false, true); } scene.updateModifiers(true); - queueEncounterMessage(scene, `${namespace}_battle_finished_1`); + queueEncounterMessage(scene, `${namespace}:option:1:finished`); }; setEncounterRewards( @@ -188,12 +188,12 @@ export const TrainingSessionEncounter: IMysteryEncounter = new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, - secondOptionPrompt: `${namespace}_option_2_select_prompt`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, + secondOptionPrompt: `${namespace}:option:2:select_prompt`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }) @@ -242,7 +242,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = scene.removePokemonFromPlayerParty(playerPokemon, false); const onBeforeRewardsPhase = () => { - queueEncounterMessage(scene, `${namespace}_battle_finished_2`); + queueEncounterMessage(scene, `${namespace}:option:2:finished`); // Add the pokemon back to party with Nature change playerPokemon.setNature(encounter.misc.chosenNature); scene.gameData.setPokemonCaught(playerPokemon, false); @@ -270,12 +270,12 @@ export const TrainingSessionEncounter: IMysteryEncounter = new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DEFAULT) .withDialogue({ - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, - secondOptionPrompt: `${namespace}_option_3_select_prompt`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + secondOptionPrompt: `${namespace}:option:3:select_prompt`, selected: [ { - text: `${namespace}_option_selected_message`, + text: `${namespace}:option:selected`, }, ], }) @@ -337,7 +337,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = scene.removePokemonFromPlayerParty(playerPokemon, false); const onBeforeRewardsPhase = () => { - queueEncounterMessage(scene, `${namespace}_battle_finished_3`); + queueEncounterMessage(scene, `${namespace}:option:3:finished`); // Add the pokemon back to party with ability change const abilityIndex = encounter.misc.abilityIndex; if (!!playerPokemon.getFusionSpeciesForm()) { diff --git a/src/data/mystery-encounters/mystery-encounter-dialogue.ts b/src/data/mystery-encounters/mystery-encounter-dialogue.ts index cbf939744c4..34f5f4eb169 100644 --- a/src/data/mystery-encounters/mystery-encounter-dialogue.ts +++ b/src/data/mystery-encounters/mystery-encounter-dialogue.ts @@ -23,12 +23,6 @@ export class EncounterOptionsDialogue { options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options } -export default class MysteryEncounterDialogue { - intro?: TextDisplay[]; - encounterOptionsDialogue?: EncounterOptionsDialogue; - outro?: TextDisplay[]; -} - /** * Example MysteryEncounterDialogue object: * @@ -72,3 +66,9 @@ export default class MysteryEncounterDialogue { } * */ +export default class MysteryEncounterDialogue { + intro?: TextDisplay[]; + encounterOptionsDialogue?: EncounterOptionsDialogue; + outro?: TextDisplay[]; +} + diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 672fb3300a4..9163c16357b 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -8,7 +8,7 @@ import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter"; import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter"; import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; -import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter"; +import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter"; import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; import IMysteryEncounter from "./mystery-encounter"; import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; @@ -159,10 +159,10 @@ const anyBiomeEncounters: MysteryEncounterType[] = [ export const mysteryEncountersByBiome = new Map([ [Biome.TOWN, []], [Biome.PLAINS, [ - MysteryEncounterType.SLEEPING_SNORLAX + MysteryEncounterType.SLUMBERING_SNORLAX ]], [Biome.GRASS, [ - MysteryEncounterType.SLEEPING_SNORLAX, + MysteryEncounterType.SLUMBERING_SNORLAX, ]], [Biome.TALL_GRASS, []], [Biome.METROPOLIS, []], @@ -214,7 +214,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.DARK_DEAL] = DarkDealEncounter; allMysteryEncounters[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightEncounter; allMysteryEncounters[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionEncounter; - allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter; + allMysteryEncounters[MysteryEncounterType.SLUMBERING_SNORLAX] = SlumberingSnorlaxEncounter; allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter; allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter; allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter; diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index a863ce46020..348b7c9f398 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -3,7 +3,7 @@ export enum MysteryEncounterType { MYSTERIOUS_CHEST, DARK_DEAL, FIGHT_OR_FLIGHT, - SLEEPING_SNORLAX, + SLUMBERING_SNORLAX, TRAINING_SESSION, DEPARTMENT_STORE_SALE, SHADY_VITAMIN_DEALER, diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index 3e15782d7f0..817daa19bf8 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -8,7 +8,7 @@ import { fieryFalloutDialogue } from "#app/locales/en/mystery-encounters/fiery-f import { fightOrFlightDialogue } from "#app/locales/en/mystery-encounters/fight-or-flight-dialogue"; import { safariZoneDialogue } from "#app/locales/en/mystery-encounters/safari-zone-dialogue"; import { shadyVitaminDealerDialogue } from "#app/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue"; -import { sleepingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/sleeping-snorlax-dialogue"; +import { slumberingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/slumbering-snorlax-dialogue"; import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/training-session-dialogue"; /** @@ -32,184 +32,11 @@ export const mysteryEncounter = { "paid_money": "You paid ₽{{amount, number}}.", "receive_money": "You received ₽{{amount, number}}!", - "department_store_sale_intro_message": "It's a lady with a ton of shopping bags.", - "department_store_sale_speaker": "Shopper", - "department_store_sale_intro_dialogue": `Hello! Are you here for\nthe amazing sales too? - $There's a special coupon that you can\nredeem for a free item during the sale! - $I have an extra one. Here you go!`, - "department_store_sale_title": "Department Store Sale", - "department_store_sale_description": "There is merchandise in every direction! It looks like there are 4 counters where you can redeem the coupon for various items. The possibilities are endless!", - "department_store_sale_query": "Which counter will you go to?", - "department_store_sale_option_1_label": "TM Counter", - "department_store_sale_option_1_tooltip": "(+) TM Shop", - "department_store_sale_option_2_label": "Vitamin Counter", - "department_store_sale_option_2_tooltip": "(+) Vitamin Shop", - "department_store_sale_option_3_label": "Battle Item Counter", - "department_store_sale_option_3_tooltip": "(+) X Item Shop", - "department_store_sale_option_4_label": "Pokéball Counter", - "department_store_sale_option_4_tooltip": "(+) Pokéball Shop", - "department_store_sale_outro": "What a deal! You should shop there more often.", - - "shady_vitamin_dealer_intro_message": "A man in a dark coat approaches you.", - "shady_vitamin_dealer_speaker": "Shady Salesman", - "shady_vitamin_dealer_intro_dialogue": `.@d{16}.@d{16}.@d{16} - $I've got the goods if you've got the money. - $Make sure your Pokémon can handle it though.`, - "shady_vitamin_dealer_title": "The Vitamin Dealer", - "shady_vitamin_dealer_description": "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.", - "shady_vitamin_dealer_query": "Which deal will choose?", - "shady_vitamin_dealer_invalid_selection": "Pokémon must be healthy enough.", - "shady_vitamin_dealer_option_1_label": "The Cheap Deal", - "shady_vitamin_dealer_option_1_tooltip": "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", - "shady_vitamin_dealer_option_2_label": "The Pricey Deal", - "shady_vitamin_dealer_option_2_tooltip": "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", - "shady_vitamin_dealer_option_selected": `The man hands you two bottles and quickly disappears. - \${{selectedPokemon}} gained {{boost1}} and {{boost2}} boosts!`, - "shady_vitamin_dealer_damage_only": `But the medicine had some side effects! - $Your {{selectedPokemon}} takes some damage...`, - "shady_vitamin_dealer_bad_poison": `But the medicine had some side effects! - $Your {{selectedPokemon}} takes some damage\nand becomes badly poisoned...`, - "shady_vitamin_dealer_poison": `But the medicine had some side effects! - $Your {{selectedPokemon}} becomes poisoned...`, - "shady_vitamin_dealer_no_bad_effects": "Looks like there were no side-effects this time.", - "shady_vitamin_dealer_option_3_label": "Leave", - "shady_vitamin_dealer_option_3_tooltip": "(-) No Rewards", - - "field_trip_intro_message": "It's a teacher and some school children!", - "field_trip_speaker": "Teacher", - "field_trip_intro_dialogue": `Hello, there! Would you be able to\nspare a minute for my students? - $I'm teaching them about Pokémon moves\nand would love to show them a demonstration. - $Would you mind showing us one of\nthe moves your Pokémon can use?`, - "field_trip_title": "Field Trip", - "field_trip_description": "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.", - "field_trip_query": "Which move category will you show off?", - "field_trip_option_1_label": "A Physical Move", - "field_trip_option_1_tooltip": "(+) Physical Item Rewards", - "field_trip_option_2_label": "A Special Move", - "field_trip_option_2_tooltip": "(+) Special Item Rewards", - "field_trip_option_3_label": "A Status Move", - "field_trip_option_3_tooltip": "(+) Status Item Rewards", - "field_trip_second_option_prompt": "Choose a move for your Pokémon to use.", - "field_trip_option_selected": "{{pokeName}} shows off an awesome display of {{move}}!", - "field_trip_option_incorrect": `... - $That isn't a {{moveCategory}} move! - $I'm sorry, but I can't give you anything.`, - "field_trip_lesson_learned": `Looks like you learned a valuable lesson? - $Your Pokémon also gained some knowledge.`, - "field_trip_outro_good": "Thank you so much for your kindness!\nI hope the items I had were helpful!", - "field_trip_outro_bad": "Come along children, we'll\nfind a better demonstration elsewhere.", - - // Mystery Encounters -- Great Tier - - "mysterious_challengers_intro_message": "Mysterious challengers have appeared!", - "mysterious_challengers_title": "Mysterious Challengers", - "mysterious_challengers_description": "If you defeat a challenger, you might impress them enough to receive a boon. But some look tough, are you up to the challenge?", - "mysterious_challengers_query": "Who will you battle?", - "mysterious_challengers_option_1_label": "A clever, mindful foe", - "mysterious_challengers_option_1_tooltip": "(-) Standard Battle\n(+) Move Item Rewards", - "mysterious_challengers_option_2_label": "A strong foe", - "mysterious_challengers_option_2_tooltip": "(-) Hard Battle\n(+) Good Rewards", - "mysterious_challengers_option_3_label": "The mightiest foe", - "mysterious_challengers_option_3_tooltip": "(-) Brutal Battle\n(+) Great Rewards", - "mysterious_challengers_option_selected_message": "The trainer steps forward...", - "mysterious_challengers_outro_win": "The mysterious challenger was defeated!", - - "safari_zone_intro_message": "It's a safari zone!", - "safari_zone_title": "The Safari Zone", - "safari_zone_description": "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of 3 wild encounters where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", - "safari_zone_query": "Would you like to enter?", - "safari_zone_option_1_label": "Enter", - "safari_zone_option_1_tooltip": "(-) Pay {{option1Money, money}}\n@[SUMMARY_GREEN]{(?) Safari Zone}", - "safari_zone_option_2_label": "Leave", - "safari_zone_option_2_tooltip": "(-) No Rewards", - "safari_zone_option_1_selected_message": "Time to test your luck!", - "safari_zone_option_2_selected_message": "You hurry along your way,\nwith a slight feeling of regret.", - "safari_zone_pokeball_option_label": "Throw a Pokéball", - "safari_zone_pokeball_option_tooltip": "(+) Throw a Pokéball", - "safari_zone_pokeball_option_selected": "You throw a Pokéball!", - "safari_zone_bait_option_label": "Throw bait", - "safari_zone_bait_option_tooltip": "(+) Increases Capture Rate\n(-) Chance to Increase Flee Rate", - "safari_zone_bait_option_selected": "You throw some bait!", - "safari_zone_mud_option_label": "Throw mud", - "safari_zone_mud_option_tooltip": "(+) Decreases Flee Rate\n(-) Chance to Decrease Capture Rate", - "safari_zone_mud_option_selected": "You throw some mud!", - "safari_zone_flee_option_label": "Flee", - "safari_zone_flee_option_tooltip": "(?) Flee from this Pokémon", - "safari_zone_pokemon_watching": "{{pokemonName}} is watching carefully!", - "safari_zone_pokemon_eating": "{{pokemonName}} is eating!", - "safari_zone_pokemon_busy_eating": "{{pokemonName}} is busy eating!", - "safari_zone_pokemon_angry": "{{pokemonName}} is angry!", - "safari_zone_pokemon_beside_itself_angry": "{{pokemonName}} is beside itself with anger!", - "safari_zone_remaining_count": "{{remainingCount}} Pokémon remaining!", - - "fiery_fallout_intro_message": "You encounter a blistering storm of smoke and ash!", - "fiery_fallout_title": "Fiery Fallout", - "fiery_fallout_description": "The whirling storm of ash and embers has cut visibility to nearly zero. It seems like there might be some... source that is causing these conditions. But what could be behind a phenomenon of this magnitude?", - "fiery_fallout_query": "What will you do?", - "fiery_fallout_option_1_label": "Find the source", - "fiery_fallout_option_1_tooltip": "(?) Discover the source\n(-) Hard Battle", - "fiery_fallout_option_2_label": "Hunker down", - "fiery_fallout_option_2_tooltip": "(-) Suffer the effects of the weather", - "fiery_fallout_option_3_label": "Your Fire types help", - "fiery_fallout_option_3_tooltip": "(+) End the conditions\n(+) Gain a Charcoal", - "fiery_fallout_option_3_disabled_tooltip": "You need at least 2 Fire Type Pokémon to choose this", - "fiery_fallout_option_1_selected": `You push through the storm, and find two Volcarona in the middle of a mating dance! - $They don't take kindly to the interruption and attack!`, - "fiery_fallout_option_2_selected": `The weather effects cause significant harm as you struggle to find shelter! - $Your party takes 30% Max HP damage! - $Your {burnTarget} also becomes burned!`, - // "fiery_fallout_boss_enraged": "The opposing {{enemyPokemon}} has become enraged!", - "fiery_fallout_option_3_selected": `Your {{primaryPokemonName}} and {{secondaryPokemonName}} guide you to where two Volcarona are in the middle of a mating dance! - $Thankfully, your Pokémon are able to calm them, and they depart without issue.`, - - // Mystery Encounters -- Ultra Tier - - "training_session_intro_message": "You've come across some\ntraining tools and supplies.", - "training_session_title": "Training Session", - "training_session_description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.", - "training_session_query": "How should you train?", - "training_session_option_1_label": "Light Training", - "training_session_option_1_tooltip": "(-) Light Battle\n(+) Improve 2 Random IVs of Pokémon", - "training_session_option_2_label": "Moderate Training", - "training_session_option_2_tooltip": "(-) Moderate Battle\n(+) Change Pokémon's Nature", - "training_session_option_2_select_prompt": "Select a new nature\nto train your Pokémon in.", - "training_session_option_3_label": "Heavy Training", - "training_session_option_3_tooltip": "(-) Harsh Battle\n(+) Change Pokémon's Ability", - "training_session_option_3_select_prompt": "Select a new ability\nto train your Pokémon in.", - "training_session_option_selected_message": "{{selectedPokemon}} moves across\nthe clearing to face you...", - "training_session_battle_finished_1": `{{selectedPokemon}} returns, feeling\nworn out but accomplished! - $Its {{stat1}} and {{stat2}} IVs were improved!`, - "training_session_battle_finished_2": `{{selectedPokemon}} returns, feeling\nworn out but accomplished! - $Its nature was changed to {{nature}}!`, - "training_session_battle_finished_3": `{{selectedPokemon}} returns, feeling\nworn out but accomplished! - $Its ability was changed to {{ability}}!`, - "training_session_outro_win": "That was a successful training session!", - - // Mystery Encounters -- Rogue Tier - - "sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path. - $You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`, - "sleeping_snorlax_title": "Sleeping Snorlax", - "sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...", - "sleeping_snorlax_query": "What will you do?", - "sleeping_snorlax_option_1_label": "Fight it", - "sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax", - "sleeping_snorlax_option_2_label": "Wait for it to move", - "sleeping_snorlax_option_2_tooltip": "@[SUMMARY_BLUE]{(75%) Wait a short time}\n@[SUMMARY_BLUE]{(25%) Wait a long time}", - "sleeping_snorlax_option_3_label": "Steal its item", - "sleeping_snorlax_option_3_tooltip": "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Leftovers", - "sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this", - "sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.", - "sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32} - $You wait for a time, but the Snorlax's yawns make your party sleepy...`, - "sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!", - "sleeping_snorlax_option_3_good_result": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!", - mysteriousChallengers: mysteriousChallengersDialogue, mysteriousChest: mysteriousChestDialogue, darkDeal: darkDealDialogue, fightOrFlight: fightOrFlightDialogue, - sleepingSnorlax: sleepingSnorlaxDialogue, + slumberingSnorlax: slumberingSnorlaxDialogue, trainingSession: trainingSessionDialogue, departmentStoreSale: departmentStoreSaleDialogue, shadyVitaminDealer: shadyVitaminDealerDialogue, diff --git a/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts index 287e3c9bae2..5b7fe9a0af0 100644 --- a/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts +++ b/src/locales/en/mystery-encounters/department-store-sale-dialogue.ts @@ -1,31 +1,29 @@ export const departmentStoreSaleDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "It's a lady with a ton of shopping bags.", + speaker: "Shopper", + intro_dialogue: `Hello! Are you here for\nthe amazing sales too? + $There's a special coupon that you can\nredeem for a free item during the sale! + $I have an extra one. Here you go!`, + title: "Department Store Sale", + description: "There is merchandise in every direction! It looks like there are 4 counters where you can redeem the coupon for various items. The possibilities are endless!", + query: "Which counter will you go to?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "TM Counter", + tooltip: "(+) TM Shop", }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Vitamin Counter", + tooltip: "(+) Vitamin Shop", }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "Battle Item Counter", + tooltip: "(+) X Item Shop", + }, + 4: { + label: "Pokéball Counter", + tooltip: "(+) Pokéball Shop", }, }, - outro: "You are back on track." + outro: "What a deal! You should shop there more often." }; diff --git a/src/locales/en/mystery-encounters/field-trip-dialogue.ts b/src/locales/en/mystery-encounters/field-trip-dialogue.ts index 7e4a4041b92..62ee175e073 100644 --- a/src/locales/en/mystery-encounters/field-trip-dialogue.ts +++ b/src/locales/en/mystery-encounters/field-trip-dialogue.ts @@ -1,31 +1,33 @@ export const fieldTripDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "It's a teacher and some school children!", + speaker: "Teacher", + intro_dialogue: `Hello, there! Would you be able to\nspare a minute for my students? + $I'm teaching them about Pokémon moves\nand would love to show them a demonstration. + $Would you mind showing us one of\nthe moves your Pokémon can use?`, + title: "Field Trip", + description: "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.", + query: "Which move category will you show off?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "A Physical Move", + tooltip: "(+) Physical Item Rewards", }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "A Special Move", + tooltip: "(+) Special Item Rewards", }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "A Status Move", + tooltip: "(+) Status Item Rewards", }, + selected: "{{pokeName}} shows off an awesome display of {{move}}!", + incorrect: `... + $That isn't a {{moveCategory}} move! + $I'm sorry, but I can't give you anything.`, + lesson_learned: `Looks like you learned a valuable lesson? + $Your Pokémon also gained some knowledge.` }, - outro: "You are back on track." + second_option_prompt: "Choose a move for your Pokémon to use.", + outro_good: "Thank you so much for your kindness!\nI hope the items I had were helpful!", + outro_bad: "Come along children, we'll\nfind a better demonstration elsewhere." }; diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts index 2c0549a3374..59808bc35ac 100644 --- a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -1,31 +1,28 @@ export const fieryFalloutDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", + intro: "You encounter a blistering storm of smoke and ash!", + title: "Fiery Fallout", + description: "The whirling ash and embers have cut visibility to nearly zero. It seems like there might be some... source that is causing these conditions. But what could be behind a phenomenon of this magnitude?", query: "What will you do?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Find the source", + tooltip: "(?) Discover the source\n(-) Hard Battle", + selected: `You push through the storm, and find two Volcarona in the middle of a mating dance! + $They don't take kindly to the interruption and attack!`, }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Hunker down", + tooltip: "(-) Suffer the effects of the weather", + selected: `The weather effects cause significant harm as you struggle to find shelter! + $Your party takes 30% Max HP damage! + $Your {burnTarget} also becomes burned!`, }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "Your Fire types help", + tooltip: "(+) End the conditions\n(+) Gain a Charcoal", + disabled_tooltip: "You need at least 2 Fire Type Pokémon to choose this", + selected: `Your {{primaryPokemonName}} and {{secondaryPokemonName}} guide you to where two Volcarona are in the middle of a mating dance! + $Thankfully, your Pokémon are able to calm them, and they depart without issue.`, }, - }, - outro: "You are back on track." + } }; diff --git a/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts index 20be45af4c9..56c78e7e5f8 100644 --- a/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts +++ b/src/locales/en/mystery-encounters/mysterious-challengers-dialogue.ts @@ -1,31 +1,22 @@ export const mysteriousChallengersDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "Mysterious challengers have appeared!", + title: "Mysterious Challengers", + description: "If you defeat a challenger, you might impress them enough to receive a boon. But some look tough, are you up to the challenge?", + query: "Who will you battle?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "A clever, mindful foe", + tooltip: "(-) Standard Battle\n(+) Move Item Rewards", }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "A strong foe", + tooltip: "(-) Hard Battle\n(+) Good Rewards", }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "The mightiest foe", + tooltip: "(-) Brutal Battle\n(+) Great Rewards", }, + selected: "The trainer steps forward...", }, - outro: "You are back on track." + outro: "The mysterious challenger was defeated!" }; diff --git a/src/locales/en/mystery-encounters/safari-zone-dialogue.ts b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts index 46952b7b197..ae77aa447b8 100644 --- a/src/locales/en/mystery-encounters/safari-zone-dialogue.ts +++ b/src/locales/en/mystery-encounters/safari-zone-dialogue.ts @@ -1,31 +1,46 @@ export const safariZoneDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "It's a safari zone!", + title: "The Safari Zone", + description: "There are all kinds of rare and special Pokémon that can be found here!\nIf you choose to enter, you'll have a time limit of 3 wild encounters where you can try to catch these special Pokémon.\n\nBeware, though. These Pokémon may flee before you're able to catch them!", + query: "Would you like to enter?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Enter", + tooltip: "(-) Pay {{option1Money, money}}\n@[SUMMARY_GREEN]{(?) Safari Zone}", + selected: "Time to test your luck!", }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, - }, - 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "Leave", + tooltip: "(-) No Rewards", + selected: "You hurry along your way,\nwith a slight feeling of regret.", }, }, - outro: "You are back on track." + safari: { + 1: { + label: "Throw a Pokéball", + tooltip: "(+) Throw a Pokéball", + selected: "You throw a Pokéball!", + }, + 2: { + label: "Throw bait", + tooltip: "(+) Increases Capture Rate\n(-) Chance to Increase Flee Rate", + selected: "You throw some bait!", + }, + 3: { + label: "Throw mud", + tooltip: "(+) Decreases Flee Rate\n(-) Chance to Decrease Capture Rate", + selected: "You throw some mud!", + }, + 4: { + label: "Flee", + tooltip: "(?) Flee from this Pokémon", + }, + watching: "{{pokemonName}} is watching carefully!", + eating: "{{pokemonName}} is eating!", + busy_eating: "{{pokemonName}} is busy eating!", + angry: "{{pokemonName}} is angry!", + beside_itself_angry: "{{pokemonName}} is beside itself with anger!", + remaining_count: "{{remainingCount}} Pokémon remaining!", + }, + outro: "That was a fun little excursion!" }; diff --git a/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts index 9bfdc139ea1..ecf88577c75 100644 --- a/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts +++ b/src/locales/en/mystery-encounters/shady-vitamin-dealer-dialogue.ts @@ -1,31 +1,40 @@ export const shadyVitaminDealerDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "A man in a dark coat approaches you.", + speaker: "Shady Salesman", + intro_dialogue: `.@d{16}.@d{16}.@d{16} + $I've got the goods if you've got the money. + $Make sure your Pokémon can handle it though.`, + title: "The Vitamin Dealer", + description: "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.", + query: "Which deal will choose?", + invalid_selection: "Pokémon must be healthy enough.", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", + label: "The Cheap Deal", + tooltip: "(-) Pay {{option1Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", + label: "The Pricey Deal", + tooltip: "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", + label: "Leave", + tooltip: "(-) No Rewards", selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. $You and your Pokémon are fatigued from the whole ordeal.`, }, + selected: `The man hands you two bottles and quickly disappears. + \${{selectedPokemon}} gained {{boost1}} and {{boost2}} boosts!` }, - outro: "You are back on track." + damage_only: `But the medicine had some side effects! + $Your {{selectedPokemon}} takes some damage...`, + bad_poison: `But the medicine had some side effects! + $Your {{selectedPokemon}} takes some damage\nand becomes badly poisoned...`, + poison: `But the medicine had some side effects! + $Your {{selectedPokemon}} becomes poisoned...`, + no_bad_effects: "Looks like there were no side-effects this time.", }; diff --git a/src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts b/src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts similarity index 100% rename from src/locales/en/mystery-encounters/sleeping-snorlax-dialogue.ts rename to src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts diff --git a/src/locales/en/mystery-encounters/training-session-dialogue.ts b/src/locales/en/mystery-encounters/training-session-dialogue.ts index a54adf92492..12e84dd8d0d 100644 --- a/src/locales/en/mystery-encounters/training-session-dialogue.ts +++ b/src/locales/en/mystery-encounters/training-session-dialogue.ts @@ -1,31 +1,30 @@ export const trainingSessionDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", - query: "What will you do?", + intro: "You've come across some\ntraining tools and supplies.", + title: "Training Session", + description: "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.", + query: "How should you train?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Light Training", + tooltip: "(-) Light Battle\n(+) Improve 2 Random IVs of Pokémon", + finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished! + $Its {{stat1}} and {{stat2}} IVs were improved!`, }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Moderate Training", + tooltip: "(-) Moderate Battle\n(+) Change Pokémon's Nature", + select_prompt: "Select a new nature\nto train your Pokémon in.", + finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished! + $Its nature was changed to {{nature}}!`, }, 3: { - label: "Wander aimlessly", + label: "Heavy Training", tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + select_prompt: "Select a new ability\nto train your Pokémon in.", + finished: `{{selectedPokemon}} returns, feeling\nworn out but accomplished! + $Its ability was changed to {{ability}}!`, }, + selected: "{{selectedPokemon}} moves across\nthe clearing to face you...", }, - outro: "You are back on track." + outro: "That was a successful training session!", }; diff --git a/src/overrides.ts b/src/overrides.ts index 7272f68bcb4..e19a5bf20dd 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; */ // 1 to 256, set to null to ignore -export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256; +export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.SLEEPING_SNORLAX; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 76a7dca8cdf..e8dcc369662 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -12,7 +12,7 @@ import GameManager from "#app/test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; -const namespace = "mysteryEncounter:lostAtSeaDialogue"; +const namespace = "mysteryEncounter:lostAtSea"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA]; const defaultBiome = Biome.SEA; @@ -123,7 +123,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); expect(blastoise.exp).toBe(expBefore + laprasSpecies.baseExp * defaultWave); - }, 10000000); + }); it("should leave encounter without battle", async () => { game.override.startingWave(33); @@ -178,7 +178,6 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - // await workaround_reInitSceneWithOverrides(game); await game.runToMysteryEncounter(defaultParty); await runSelectMysteryEncounterOption(game, 2); From b68abaab9415ee97cfeef871c45c38cdb00faf85 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 16:01:47 -0400 Subject: [PATCH 09/27] cleanup and add jsdocs --- src/data/battle-anims.ts | 50 ++++++++++++------- .../encounters/safari-zone-encounter.ts | 14 +++--- .../slumbering-snorlax-encounter.ts | 40 ++++++++------- .../slumbering-snorlax-dialogue.ts | 41 +++++++-------- 4 files changed, 80 insertions(+), 65 deletions(-) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index f832318fc4b..234e827b648 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -103,6 +103,11 @@ export enum CommonAnim { LOCK_ON = 2120 } +/** + * Animations used for Mystery Encounters + * These are custom animations that may or may not work in any other circumstance + * Use at your own risk + */ export enum EncounterAnim { MAGMA_BG, MAGMA_SPOUT @@ -520,23 +525,26 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { }); } -export function initEncounterAnims(scene: BattleScene, anims: EncounterAnim | EncounterAnim[]): Promise { +/** + * Fetches animation configs to be used in a Mystery Encounter + * @param scene + * @param anims - one or more animations to fetch + */ +export async function initEncounterAnims(scene: BattleScene, anims: EncounterAnim | EncounterAnim[]): Promise { anims = anims instanceof Array ? anims : [anims]; - return new Promise(resolve => { - const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); - const encounterAnimIds = Utils.getEnumValues(EncounterAnim); - const encounterAnimFetches = []; - for (const anim of anims) { - if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { - continue; - } - const encounterAnimId = encounterAnimIds[anim]; - encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) - .then(response => response.json()) - .then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas)))); + const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); + const encounterAnimIds = Utils.getEnumValues(EncounterAnim); + const encounterAnimFetches = []; + for (const anim of anims) { + if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { + continue; } - Promise.allSettled(encounterAnimFetches).then(() => resolve()); - }); + const encounterAnimId = encounterAnimIds[anim]; + encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) + .then(response => response.json()) + .then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas)))); + } + await Promise.allSettled(encounterAnimFetches); } export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise { @@ -593,10 +601,14 @@ export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): P }); } -export function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise { - return new Promise(resolve => { - loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad).then(() => resolve()); - }); +/** + * Loads encounter animation assets to scene + * MUST be called after [initEncounterAnims()](./battle-anims.ts) to load all required animations properly + * @param scene + * @param startLoad + */ +export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise { + await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad); } export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise { diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index ee0a161408a..164ac112596 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -19,6 +19,8 @@ import { getPokemonNameWithAffix } from "#app/messages"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:safariZone"; +const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768]; + /** * Safari Zone encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39} @@ -314,14 +316,14 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); - scene.time.delayedCall(512, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => { scene.playSound("pb_throw"); // Trainer throw frames scene.trainer.setFrame("2"); - scene.time.delayedCall(184, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => { scene.trainer.setFrame("3"); - scene.time.delayedCall(768, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); }); }); @@ -380,14 +382,14 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); - scene.time.delayedCall(512, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => { scene.playSound("pb_throw"); // Trainer throw frames scene.trainer.setFrame("2"); - scene.time.delayedCall(184, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => { scene.trainer.setFrame("3"); - scene.time.delayedCall(768, () => { + scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => { scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`); }); }); diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 0e60304d9dc..2efae68c9eb 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -16,16 +16,16 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { PartyHealPhase } from "#app/phases"; /** i18n namespace for the encounter */ -const namespace = "mysteryEncounter:sleeping_snorlax"; +const namespace = "mysteryEncounter:slumberingSnorlax"; /** * Sleeping Snorlax encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ -export const SleepingSnorlaxEncounter: IMysteryEncounter = +export const SlumberingSnorlaxEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.SLEEPING_SNORLAX + MysteryEncounterType.SLUMBERING_SNORLAX ) .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 @@ -44,7 +44,7 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = ]) .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) .withOnInit((scene: BattleScene) => { @@ -70,16 +70,16 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = return true; }) - .withTitle(`${namespace}_title`) - .withDescription(`${namespace}_description`) - .withQuery(`${namespace}_query`) + .withTitle(`${namespace}:title`) + .withDescription(`${namespace}:description`) + .withQuery(`${namespace}:query`) .withSimpleOption( { - buttonLabel: `${namespace}_option_1_label`, - buttonTooltip: `${namespace}_option_1_tooltip`, + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, selected: [ { - text: `${namespace}_option_1_selected_message`, + text: `${namespace}:option:1:selected`, }, ], }, @@ -105,11 +105,11 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = ) .withSimpleOption( { - buttonLabel: `${namespace}_option_2_label`, - buttonTooltip: `${namespace}_option_2_tooltip`, + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, selected: [ { - text: `${namespace}_option_2_selected_message`, + text: `${namespace}:option:2:selected`, }, ], }, @@ -117,7 +117,7 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = // Fall asleep waiting for Snorlax // Full heal party scene.unshiftPhase(new PartyHealPhase(scene, true)); - queueEncounterMessage(scene, `${namespace}_option_2_good_result`); + queueEncounterMessage(scene, `${namespace}:option:2:rest_result`); leaveEncounterWithoutBattle(scene); } ) @@ -126,15 +126,19 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) .withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) .withDialogue({ - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, - disabledButtonTooltip: `${namespace}_option_3_disabled_tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`, + selected: [ + { + text: `${namespace}:option:3:selected` + } + ] }) .withOptionPhase(async (scene: BattleScene) => { // Steal the Snorlax's Leftovers const instance = scene.currentBattle.mysteryEncounter; setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false }); - queueEncounterMessage(scene, `${namespace}_option_3_good_result`); // Snorlax exp to Pokemon that did the stealing setEncounterExp(scene, instance.primaryPokemon.id, getPokemonSpecies(Species.SNORLAX).baseExp); leaveEncounterWithoutBattle(scene); diff --git a/src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts b/src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts index bc2dc65e572..92244573c9b 100644 --- a/src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts +++ b/src/locales/en/mystery-encounters/slumbering-snorlax-dialogue.ts @@ -1,31 +1,28 @@ -export const sleepingSnorlaxDialogue = { - intro: "Wandering aimlessly through the sea, you've effectively gotten nowhere.", - title: "Lost at Sea", - description: "The sea is turbulent in this area, and you're running out of energy.\nThis is bad. Is there a way out of the situation?", +export const slumberingSnorlaxDialogue = { + intro: `As you walk down a narrow pathway, you see a towering silhouette blocking your path. + $You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`, + title: "Slumbering Snorlax", + description: "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...", query: "What will you do?", option: { 1: { - label: "{{option1PrimaryName}} can help", - label_disabled: "Can't {{option1RequiredMove}}", - tooltip: "(+) {{option1PrimaryName}} saves you\n(+) {{option1PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option1RequiredMove}} on", - selected: `{{option1PrimaryName}} swims ahead, guiding you back on track. - \${{option1PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Battle it", + tooltip: "(-) Fight Sleeping Snorlax\n(+) Special Reward", + selected: "You approach the\nPokémon without fear.", }, 2: { - label: "{{option2PrimaryName}} can help", - label_disabled: "Can't {{option2RequiredMove}}", - tooltip: "(+) {{option2PrimaryName}} saves you\n(+) {{option2PrimaryName}} gains some EXP", - tooltip_disabled: "You have no Pokémon to {{option2RequiredMove}} with", - selected: `{{option2PrimaryName}} flies ahead of your boat, guiding you back on track. - \${{option2PrimaryName}} seems to also have gotten stronger in this time of need!`, + label: "Wait for it to move", + tooltip: "(-) Wait a Long Time\n(+) Recover Party", + selected: `.@d{32}.@d{32}.@d{32} + $You wait for a time, but the Snorlax's yawns make your party sleepy...`, + rest_result: "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!", }, 3: { - label: "Wander aimlessly", - tooltip: "(-) Each of your Pokémon lose {{damagePercentage}}% of their total HP", - selected: `You float about in the boat, steering without direction until you finally spot a landmark you remember. - $You and your Pokémon are fatigued from the whole ordeal.`, + label: "Steal its item", + tooltip: "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Special Reward", + disabled_tooltip: "Your Pokémon need to know certain moves to choose this", + selected: `Your {{option3PrimaryName}} uses {{option3PrimaryMove}}! + $@s{item_fanfare}It steals Leftovers off the sleeping\nSnorlax and you make out like bandits!`, }, - }, - outro: "You are back on track." + } }; From 70fc4b67ae12ce6d997cdd89f3187608ce7f411e Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 22:37:58 -0400 Subject: [PATCH 10/27] finish fiery fallout encounter --- public/battle-anims/encounter-magma-bg.json | 8 +- .../battle-anims/encounter-magma-spout.json | 113 +++++++++++------- public/images/mystery-encounters/exclaim.json | 41 +++++++ public/images/mystery-encounters/exclaim.png | Bin 0 -> 378 bytes src/battle-scene.ts | 31 ++--- src/data/battle-anims.ts | 52 +++----- .../department-store-sale-encounter.ts | 2 +- .../encounters/field-trip-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 95 +++++++++++---- .../encounters/mysterious-chest-encounter.ts | 2 +- .../mystery-encounters/mystery-encounter.ts | 39 ++++-- .../utils/encounter-phase-utils.ts | 62 ++++++++-- .../utils/encounter-pokemon-utils.ts | 5 +- src/field/arena.ts | 2 +- src/field/mystery-encounter-intro.ts | 10 ++ .../fiery-fallout-dialogue.ts | 10 +- src/phases.ts | 26 +++- src/phases/mystery-encounter-phases.ts | 6 +- 18 files changed, 351 insertions(+), 155 deletions(-) create mode 100644 public/images/mystery-encounters/exclaim.json create mode 100644 public/images/mystery-encounters/exclaim.png diff --git a/public/battle-anims/encounter-magma-bg.json b/public/battle-anims/encounter-magma-bg.json index 1e144b9d4f6..bb22f721d9a 100644 --- a/public/battle-anims/encounter-magma-bg.json +++ b/public/battle-anims/encounter-magma-bg.json @@ -1,5 +1,4 @@ { - "graphic": "Encounter Magma Bg", "frames": [ [], [], @@ -27,9 +26,6 @@ [], [], [], - [], - [], - [], [] ], "frameTimedEvents": { @@ -49,7 +45,7 @@ "bgX": 0, "bgY": 0, "opacity": 255, - "duration": 4, + "duration": 12, "eventType": "AnimTimedUpdateBgEvent" } ], @@ -60,7 +56,7 @@ "bgX": 0, "bgY": 0, "opacity": 0, - "duration": 7, + "duration": 8, "eventType": "AnimTimedUpdateBgEvent" } ] diff --git a/public/battle-anims/encounter-magma-spout.json b/public/battle-anims/encounter-magma-spout.json index 9e0acea61fa..21f3bec585f 100644 --- a/public/battle-anims/encounter-magma-spout.json +++ b/public/battle-anims/encounter-magma-spout.json @@ -11,7 +11,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -23,7 +23,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -35,6 +35,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -48,7 +49,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -60,7 +61,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -72,6 +73,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -85,7 +87,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -97,7 +99,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -109,6 +111,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -122,7 +125,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -134,7 +137,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -146,6 +149,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -159,7 +163,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -171,7 +175,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -183,6 +187,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -196,7 +201,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -208,7 +213,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -220,6 +225,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -233,7 +239,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -245,7 +251,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -257,6 +263,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -270,7 +277,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -282,7 +289,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -294,6 +301,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -307,7 +315,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -319,6 +327,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -330,6 +339,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -343,7 +353,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -355,6 +365,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -366,7 +377,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -380,6 +391,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -391,6 +403,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -402,7 +415,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -416,6 +429,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -427,7 +441,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -439,7 +453,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -453,7 +467,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -465,7 +479,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -477,6 +491,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -490,7 +505,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -502,6 +517,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -513,6 +529,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -526,7 +543,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -538,6 +555,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -549,7 +567,8 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, + "priority": 4, "focus": 1 } ], @@ -563,7 +582,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -575,7 +594,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -587,6 +606,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -600,7 +620,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -612,7 +632,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -624,6 +644,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -637,7 +658,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -649,7 +670,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -661,6 +682,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -674,7 +696,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -686,7 +708,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -698,6 +720,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -711,7 +734,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -723,7 +746,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -735,6 +758,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -748,7 +772,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -760,7 +784,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -772,6 +796,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -785,7 +810,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -797,7 +822,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -809,6 +834,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -822,7 +848,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -834,7 +860,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -846,6 +872,7 @@ "target": 2, "graphicFrame": 0, "opacity": 140, + "priority": 4, "focus": 1 } ] diff --git a/public/images/mystery-encounters/exclaim.json b/public/images/mystery-encounters/exclaim.json new file mode 100644 index 00000000000..31231910097 --- /dev/null +++ b/public/images/mystery-encounters/exclaim.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "exclaim.png", + "format": "RGBA8888", + "size": { + "w": 32, + "h": 32 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 32 + }, + "frame": { + "x": 0, + "y": 0, + "w": 32, + "h": 32 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$" + } +} diff --git a/public/images/mystery-encounters/exclaim.png b/public/images/mystery-encounters/exclaim.png new file mode 100644 index 0000000000000000000000000000000000000000..a7727f4da2e1dc8c14f2b3edfb42fdd0220e7b78 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^TvcF`%+TnbgQKj8-7F(Bd!U0(a&+d*#CJdWpQUrE#PF$q1;0j~Boqh6*8=E^D zAGv8-?rV>%`+hIBgIOa}a`G&Ji9jQkW*=v{`>FQ!H{(SX3}QkpHLYvEvl!2r5Erna znc+lHk3d6a*{7l-x9vm1g3=tySazM~uw+WJ^kWw=VtC>(LwSO(yV;Bhk_MGM8VX^` zX9N|#E=W*4UbM-X!FJV&tuiTm3@6nTZfG07_XGwRUuuMBny)W|7Ld&W#2|1fcru9c MboFyt=akR{04;@z`Tzg` literal 0 HcmV?d00001 diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 6cde5929308..954b24fd10b 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,43 +1,42 @@ import Phaser from "phaser"; import UI from "./ui/ui"; -import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from "./phases"; -import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon"; -import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species"; -import {Constructor, isNullOrUndefined} from "#app/utils"; +import { LevelCapPhase, LoginPhase, MessagePhase, MovePhase, NewBiomeEncounterPhase, NextEncounterPhase, ReturnPhase, SelectBiomePhase, ShowTrainerPhase, SwitchPhase, TitlePhase, TurnInitPhase } from "./phases"; +import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon"; +import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species"; +import { Constructor, isNullOrUndefined } from "#app/utils"; import * as Utils from "./utils"; -import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; +import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, TerastallizeModifier } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { Phase } from "./phase"; import { initGameSpeed } from "./system/game-speed"; import { Arena, ArenaBase } from "./field/arena"; import { GameData } from "./system/game-data"; -import { TextStyle, addTextObject, getTextColor } from "./ui/text"; +import { addTextObject, getTextColor, TextStyle } from "./ui/text"; import { allMoves } from "./data/move"; -import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, PokemonHeldItemModifierType } from "./modifier/modifier-type"; +import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, ModifierPoolType, PokemonHeldItemModifierType } from "./modifier/modifier-type"; import AbilityBar from "./ui/ability-bar"; -import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability"; -import { allAbilities } from "./data/ability"; +import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr } from "./data/ability"; import Battle, { BattleType, FixedBattleConfig } from "./battle"; import { GameMode, GameModes, getGameMode } from "./game-mode"; import FieldSpritePipeline from "./pipelines/field-sprite"; import SpritePipeline from "./pipelines/sprite"; import PartyExpBar from "./ui/party-exp-bar"; -import { TrainerSlot, trainerConfigs } from "./data/trainer-config"; +import { trainerConfigs, TrainerSlot } from "./data/trainer-config"; import Trainer, { TrainerVariant } from "./field/trainer"; import TrainerData from "./system/trainer-data"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { pokemonPrevolutions } from "./data/pokemon-evolutions"; import PokeballTray from "./ui/pokeball-tray"; import InvertPostFX from "./pipelines/invert"; -import { Achv, ModifierAchv, MoneyAchv, achvs } from "./system/achv"; +import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv"; import { Voucher, vouchers } from "./system/voucher"; import { Gender } from "./data/gender"; import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import { addUiThemeOverrides } from "./ui/ui-theme"; import PokemonData from "./system/pokemon-data"; import { Nature } from "./data/nature"; -import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from "./data/pokemon-forms"; +import { pokemonFormChanges, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms"; import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase"; import { getTypeRgb } from "./data/type"; import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; @@ -50,8 +49,8 @@ import CandyBar from "./ui/candy-bar"; import { Variant, variantData } from "./data/variant"; import { Localizable } from "#app/interfaces/locales"; import * as Overrides from "./overrides"; -import {InputsController} from "./inputs-controller"; -import {UiInputs} from "./ui-inputs"; +import { InputsController } from "./inputs-controller"; +import { UiInputs } from "./ui-inputs"; import { NewArenaEvent } from "./events/battle-scene"; import ArenaFlyout from "./ui/arena-flyout"; import { EaseType } from "#enums/ease-type"; @@ -68,7 +67,7 @@ import { UiTheme } from "#enums/ui-theme"; import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; -import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; +import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -1039,6 +1038,8 @@ export default class BattleScene extends SceneBase { const playerField = this.getPlayerField(); + this.newArena(Biome.VOLCANO); + if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) { battleConfig = this.gameMode.getFixedBattle(newWaveIndex); newDouble = battleConfig.double; diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 234e827b648..81536d74771 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -718,14 +718,16 @@ export abstract class BattleAnim { public target: Pokemon; public sprites: Phaser.GameObjects.Sprite[]; public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle; + public playOnEmptyField: boolean; private srcLine: number[]; private dstLine: number[]; - constructor(user: Pokemon, target: Pokemon) { + constructor(user: Pokemon, target: Pokemon, playOnEmptyField: boolean = false) { this.user = user; this.target = target; this.sprites = []; + this.playOnEmptyField = playOnEmptyField; } abstract getAnim(): AnimConfig; @@ -799,7 +801,7 @@ export abstract class BattleAnim { const user = !isOppAnim ? this.user : this.target; const target = !isOppAnim ? this.target : this.user; - if (!target.isOnField()) { + if (!target.isOnField() && !this.playOnEmptyField) { if (callback) { callback(); } @@ -1097,10 +1099,7 @@ export abstract class BattleAnim { let r = anim.frames.length; let f = 0; - const fieldSprites = scene.field.getAll(); - const playerFieldSprite = fieldSprites[1]; - const enemyFieldSprite = fieldSprites[3]; - const trainerSprite = fieldSprites[5]; + const existingFieldSprites = [...scene.field.getAll()]; scene.tweens.addCounter({ duration: Utils.getFrameMs(3) * frameTimeMult, @@ -1127,41 +1126,24 @@ export abstract class BattleAnim { const graphicIndex = g++; const moveSprite = sprites[graphicIndex]; - if (spritePriorities[graphicIndex] !== frame.priority) { - spritePriorities[graphicIndex] = frame.priority; + spritePriorities[graphicIndex] = frame.priority; + if (!isNullOrUndefined(frame.priority)) { const setSpritePriority = (priority: integer) => { - if (priority < 0) { - // Move to top of scene - scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); - } else if (priority === 1) { - // Move above player field - if (playerFieldSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, playerFieldSprite); + try { + if (existingFieldSprites.length > priority) { + // Move to specified priority index + scene.field.moveTo(moveSprite, scene.field.getIndex(existingFieldSprites[priority])); } else { - setSpritePriority(-1); + // Move to top of scene + scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); } - } else if (priority === 3) { - // Move above player enemy field - if (enemyFieldSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, enemyFieldSprite); - } else { - setSpritePriority(-1); - } - } else if (priority === 5) { - // Move above player trainer sprite - if (trainerSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, trainerSprite); - } else { - setSpritePriority(-1); - } - } else { - setSpritePriority(-1); + } catch (ignored) { + console.log("index is no longer valid"); } }; setSpritePriority(frame.priority); } moveSprite.setFrame(frame.graphicFrame); - //console.log(AnimFocus[frame.focus]); const graphicFrameData = frameData.get(frame.target).get(graphicIndex); moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); @@ -1219,8 +1201,8 @@ export abstract class BattleAnim { export class CommonBattleAnim extends BattleAnim { public commonAnim: CommonAnim; - constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) { - super(user, target || user); + constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) { + super(user, target || user, playOnEmptyField); this.commonAnim = commonAnim; } diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index ae025a909b1..2e8a5ff67bd 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -48,7 +48,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = speaker: `${namespace}:speaker`, }, ]) - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index da0016db360..45ac52eb161 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -56,7 +56,7 @@ export const FieldTripEncounter: IMysteryEncounter = speaker: `${namespace}:speaker`, }, ]) - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index f4c9db5e4f8..2a7c26995d3 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -1,5 +1,5 @@ import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { applyDamageToPokemon, EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { modifierTypes, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; @@ -14,25 +14,33 @@ import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#app/data/weather"; -import { randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { StatusEffect } from "#app/data/status-effect"; +import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fieryFallout"; +/** + * Damage percentage taken when suffering the heat. + * Can be a number between `0` - `100`. + * The higher the more damage taken (100% = instant KO). + */ +const DAMAGE_PERCENTAGE: number = 20; + /** * Fiery Fallout encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ export const FieryFalloutEncounter: IMysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.FIERY_FALLOUT - ) + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(40, 180) // waves 10 to 180 .withCatchAllowed(true) .withIntroSpriteConfigs([]) // Set in onInit() .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) + .withAutoHideIntroVisuals(false) .withIntroDialogue([ { text: `${namespace}:intro`, @@ -44,7 +52,6 @@ export const FieryFalloutEncounter: IMysteryEncounter = // Calculate boss mons const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.25, pokemonConfigs: [ { species: volcaronaSpecies, @@ -62,12 +69,31 @@ export const FieryFalloutEncounter: IMysteryEncounter = }; encounter.enemyPartyConfigs = [config]; - // Sets weather for 5 turns - scene.arena.trySetWeather(WeatherType.SUNNY, true); + // Load hidden Volcarona sprites + encounter.spriteConfigs = [ + { + spriteKey: volcaronaSpecies.getSpriteId(false), + fileRoot: "pokemon", + repeat: true, + hidden: true, + hasShadow: true, + x: -20 + }, + { + spriteKey: volcaronaSpecies.getSpriteId(true ), + fileRoot: "pokemon", + repeat: true, + hidden: true, + hasShadow: true, + x: 20 + }, + ]; // Load animations/sfx for Volcarona moves initCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]); + scene.arena.trySetWeather(WeatherType.SUNNY, true); + return true; }) .withOnVisualsStart((scene: BattleScene) => { @@ -75,13 +101,13 @@ export const FieryFalloutEncounter: IMysteryEncounter = const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); background.playWithoutTargets(scene, 200, 70, 2, 3); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); - animation.playWithoutTargets(scene, 100, 100, 2); - const increment = 600; - for (let i = 3; i < 6; i++) { - scene.time.delayedCall((increment) * (i - 2), () => { - animation.playWithoutTargets(scene, randSeedInt(12) * 15, 150 - randSeedInt(10) * 15, 2); - }); - } + animation.playWithoutTargets(scene, 80, 100, 2); + scene.time.delayedCall(600, () => { + animation.playWithoutTargets(scene, -20, 100, 2); + }); + scene.time.delayedCall(1200, () => { + animation.playWithoutTargets(scene, 140, 150, 2); + }); return true; }) @@ -142,15 +168,36 @@ export const FieryFalloutEncounter: IMysteryEncounter = ], }, async (scene: BattleScene) => { - // Damage party and burn 1 random member + // Damage non-fire types and burn 1 random non-fire type member + const encounter = scene.currentBattle.mysteryEncounter; + const nonFireTypes = scene.getParty().filter((p) => !p.getTypes().includes(Type.FIRE)); + + for (const pkm of nonFireTypes) { + const percentage = DAMAGE_PERCENTAGE / 100; + const damage = Math.floor(pkm.getMaxHp() * percentage); + applyDamageToPokemon(scene, pkm, damage); + } + + // Burn random member + const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status?.effect === StatusEffect.BURN); + if (burnable?.length > 0) { + const chosenPokemon = burnable[randSeedInt(burnable.length - 1)]; + if (chosenPokemon.trySetStatus(StatusEffect.BURN)) { + // Burn applied + encounter.setDialogueToken("burnedPokemon", chosenPokemon.name); + queueEncounterMessage(scene, `${namespace}:option:2:target_burned`); + } + } + // No rewards - leaveEncounterWithoutBattle(scene); + leaveEncounterWithoutBattle(scene, true); } ) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically + .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, @@ -161,12 +208,20 @@ export const FieryFalloutEncounter: IMysteryEncounter = }, ], }) + .withPreOptionPhase(async (scene: BattleScene) => { + transitionMysteryEncounterIntroVisuals(scene, false, false, 2000); + }) .withOptionPhase(async (scene: BattleScene) => { // Fire types help calm the Volcarona - // const encounter = scene.currentBattle.mysteryEncounter; + const encounter = scene.currentBattle.mysteryEncounter; + transitionMysteryEncounterIntroVisuals(scene); const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); - setEncounterExp(scene, scene.getParty().map(p => p.id), 500); + + const primary = encounter.options[2].primaryPokemon; + const secondary = encounter.options[2].secondaryPokemon[0]; + + setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 67ca533e893..a93df6cb264 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -20,7 +20,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 180) // waves 2 to 180 - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withIntroSpriteConfigs([ { spriteKey: "chest_blue", diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 5b65cb12260..289800d0b52 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -60,13 +60,23 @@ export default interface IMysteryEncounter { encounterTier?: MysteryEncounterTier; encounterAnimations?: EncounterAnim[]; hideBattleIntroMessage?: boolean; - hideIntroVisuals?: boolean; + autoHideIntroVisuals?: boolean; catchAllowed?: boolean; maxAllowedEncounters?: number; - doEncounterExp?: (scene: BattleScene) => boolean; - doEncounterRewards?: (scene: BattleScene) => boolean; + + /** + * Event callback functions + */ + /** Event when Encounter is first loaded, use it for data conditioning */ onInit?: (scene: BattleScene) => boolean; + /** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */ onVisualsStart?: (scene: BattleScene) => boolean; + /** Event right before MysteryEncounterPhase begins. Use for unshifting any phases before the actual encounter */ + onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; + /** Will provide the player party EXP before rewards are displayed for that wave */ + doEncounterExp?: (scene: BattleScene) => boolean; + /** Will provide the player a rewards shop for that wave */ + doEncounterRewards?: (scene: BattleScene) => boolean; /** * Requirements @@ -171,7 +181,7 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.encounterVariant = MysteryEncounterVariant.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; - this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true; + this.autoHideIntroVisuals = !isNullOrUndefined(this.autoHideIntroVisuals) ? this.autoHideIntroVisuals : true; this.startOfBattleEffects = this.startOfBattleEffects ?? []; // Reset any dirty flags or encounter data @@ -377,10 +387,13 @@ export class MysteryEncounterBuilder implements Partial { secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; excludePrimaryFromSupportRequirements?: boolean; dialogueTokens?: Record; + doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; onVisualsStart?: (scene: BattleScene) => boolean; + onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; + hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; enemyPartyConfigs?: EnemyPartyConfig[] = []; @@ -612,6 +625,18 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { onInit: onInit }); } + /** + * Event callback right before MysteryEncounterPhase begins. + * Use for unshifting any last-minute phases before the actual encounter, as all phases are cleared and reset at that point. + * Example: set the weather before encounter begins + * + * @param onVisualsStart - synchronous callback function to perform immediately before MysteryEncounterPhase begins + * @returns + */ + withOnPreMysteryEncounterPhase(onPreMysteryEncounterPhase: (scene: BattleScene) => boolean): this & Required> { + return Object.assign(this, { onPreMysteryEncounterPhase: onPreMysteryEncounterPhase }); + } + /** * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in * @@ -651,11 +676,11 @@ export class MysteryEncounterBuilder implements Partial { } /** - * @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter + * @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter * @returns */ - withHideIntroVisuals(hideIntroVisuals: boolean): this & Required> { - return Object.assign(this, { hideIntroVisuals: hideIntroVisuals }); + withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required> { + return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals }); } /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 44b9f7c8c7e..ef542971057 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -30,6 +30,29 @@ import { Gender } from "#app/data/gender"; import { Moves } from "#enums/moves"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +export function doTrainerExclamation(scene: BattleScene) { + const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim"); + exclamationSprite.setName("exclamation"); + scene.field.add(exclamationSprite); + scene.field.moveTo(exclamationSprite, scene.field.list.length - 1); + exclamationSprite.setVisible(true); + exclamationSprite.setPosition(110, 68); + scene.tweens.add({ + targets: exclamationSprite, + y: "-=25", + ease: "Cubic.easeOut", + duration: 300, + yoyo: true, + onComplete: () => { + scene.time.delayedCall(800, () => { + scene.field.remove(exclamationSprite, true); + }); + } + }); + + scene.playSound("GEN8- Exclaim.wav"); +} + export interface EnemyPokemonConfig { species: PokemonSpecies; isBoss: boolean; @@ -452,7 +475,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel()); const partyMemberExp = []; - let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1); + let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1; if (participantIds?.length > 0) { if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) { @@ -590,23 +613,40 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: } } -export function hideMysteryEncounterIntroVisuals(scene: BattleScene): Promise { +/** + * + * @param scene + * @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 duration + */ +export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: boolean = true, destroy: boolean = true, duration: number = 750): Promise { return new Promise(resolve => { const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals; if (introVisuals) { - // Hide + if (!hide) { + // Make sure visuals are in proper state for showing + introVisuals.setVisible(true); + introVisuals.x += 16; + introVisuals.y -= 16; + introVisuals.alpha = 0; + } + + // Transition scene.tweens.add({ targets: introVisuals, - x: "+=16", - y: "-=16", - alpha: 0, + x: hide ? "+=16" : "-=16", + y: hide ? "-=16" : "+=16", + alpha: hide ? 0 : 1, ease: "Sine.easeInOut", - duration: 750, + duration: duration, onComplete: () => { - scene.field.remove(introVisuals); - introVisuals.setVisible(false); - introVisuals.destroy(); - scene.currentBattle.mysteryEncounter.introVisuals = null; + if (hide && destroy) { + scene.field.remove(introVisuals); + introVisuals.setVisible(false); + introVisuals.destroy(); + scene.currentBattle.mysteryEncounter.introVisuals = null; + } resolve(true); } }); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 15bf282d4df..b505c4f971d 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -405,8 +405,9 @@ function removePb(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite) { }); } -export function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise { - return new Promise(resolve => { +export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise { + await new Promise(resolve => { + scene.playSound("flee"); // Ease pokemon out scene.tweens.add({ targets: pokemon, diff --git a/src/field/arena.ts b/src/field/arena.ts index b4c474ce474..d767f84a135 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -317,7 +317,7 @@ export class Arena { this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft)); if (this.weather) { - this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); + this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1), true)); this.scene.queueMessage(getWeatherStartMessage(weather)); } else { this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)); diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index bacf107f482..fb894869adf 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -32,6 +32,8 @@ export class MysteryEncounterSpriteConfig { disableAnimation?: boolean = false; /** Repeat the animation. Defaults to `false` */ repeat?: boolean = false; + /** Hidden at start of encounter. Defaults to `false` */ + hidden?: boolean = false; /** Tint color. `0` - `1`. Higher means darker tint. */ tint?: number; /** X offset */ @@ -105,6 +107,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con tintSprite = getItemSprite(spriteKey); } + sprite.setVisible(!config.hidden); tintSprite.setVisible(false); if (scale) { @@ -345,6 +348,13 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.untint(tintSprite, duration, ease); }); } + + setVisible(value: boolean): this { + this.getSprites().forEach(sprite => { + sprite.setVisible(value); + }); + return super.setVisible(value); + } } export default interface MysteryEncounterIntroVisuals { diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts index 59808bc35ac..6f93cb239b1 100644 --- a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -13,16 +13,16 @@ export const fieryFalloutDialogue = { 2: { label: "Hunker down", tooltip: "(-) Suffer the effects of the weather", - selected: `The weather effects cause significant harm as you struggle to find shelter! - $Your party takes 30% Max HP damage! - $Your {burnTarget} also becomes burned!`, + selected: `The weather effects cause significant\nharm as you struggle to find shelter! + $Your party takes 20% Max HP damage!`, + target_burned: "Your {{burnedPokemon}} also became burned!" }, 3: { label: "Your Fire types help", tooltip: "(+) End the conditions\n(+) Gain a Charcoal", disabled_tooltip: "You need at least 2 Fire Type Pokémon to choose this", - selected: `Your {{primaryPokemonName}} and {{secondaryPokemonName}} guide you to where two Volcarona are in the middle of a mating dance! - $Thankfully, your Pokémon are able to calm them, and they depart without issue.`, + selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance! + $Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`, }, } }; diff --git a/src/phases.ts b/src/phases.ts index 5e4bdf29943..2e6c8224de2 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -67,7 +67,7 @@ import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { doTrainerExclamation, handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -901,6 +901,15 @@ export class EncounterPhase extends BattlePhase { battle.mysteryEncounter = newEncounter; } loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter.introVisuals.initSprite())); + // Load Mystery Encounter Exclamation bubble and sfx + loadEnemyAssets.push(new Promise(resolve => { + this.scene.loadSe("GEN8- Exclaim.wav", "battle_anims", "GEN8- Exclaim.wav"); + this.scene.loadAtlas("exclaim", "mystery-encounters"); + this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve()); + if (!this.scene.load.isLoading()) { + this.scene.load.start(); + } + })); } else { // This block only applies for double battles to init the boss segments (idk why it's split up like this) if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { @@ -1091,8 +1100,13 @@ export class EncounterPhase extends BattlePhase { const doShowEncounterOptions = () => { this.scene.ui.clearText(); this.scene.ui.getMessageHandler().hideNameText(); - this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); + // Can add any additional unshift phases here before MysteryEncounterPhase begins (and all phase queues are cleared) + if (this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase) { + this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase(this.scene); + } + + this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); this.end(); }; @@ -1125,6 +1139,7 @@ export class EncounterPhase extends BattlePhase { if (!encounterMessage) { doEncounter(); } else { + doTrainerExclamation(this.scene); this.scene.ui.showDialogue(encounterMessage, "???", null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doEncounter())); }); @@ -2706,12 +2721,14 @@ export class NewBattlePhase extends BattlePhase { export class CommonAnimPhase extends PokemonPhase { private anim: CommonAnim; private targetIndex: integer; + private playOnEmptyField: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim, playOnEmptyField: boolean = false) { super(scene, battlerIndex); this.anim = anim; this.targetIndex = targetIndex; + this.playOnEmptyField = playOnEmptyField; } setAnimation(anim: CommonAnim) { @@ -2719,7 +2736,8 @@ export class CommonAnimPhase extends PokemonPhase { } start() { - new CommonBattleAnim(this.anim, this.getPokemon(), this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon()).play(this.scene, () => { + const target = this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon(); + new CommonBattleAnim(this.anim, this.getPokemon(), target, this.playOnEmptyField).play(this.scene, () => { this.end(); }); } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 6f474500838..1ac00cbea8e 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -2,7 +2,7 @@ import i18next from "i18next"; import BattleScene from "../battle-scene"; import { Phase } from "../phase"; import { Mode } from "../ui/ui"; -import { hideMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; +import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases"; import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter"; @@ -148,8 +148,8 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { start() { super.start(); - if (this.scene.currentBattle.mysteryEncounter.hideIntroVisuals) { - hideMysteryEncounterIntroVisuals(this.scene).then(() => { + if (this.scene.currentBattle.mysteryEncounter.autoHideIntroVisuals) { + transitionMysteryEncounterIntroVisuals(this.scene).then(() => { this.scene.executeWithSeedOffset(() => { this.onOptionSelect(this.scene).finally(() => { this.end(); From a598c1cb059facdcca8a5df578264939d3f9ff33 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 22:56:01 -0400 Subject: [PATCH 11/27] fix unit test breaks --- src/battle-scene.ts | 2 -- src/data/mystery-encounters/utils/encounter-phase-utils.ts | 4 ++-- .../encounters/lost-at-sea-encounter.test.ts | 4 ++-- src/test/utils/mocks/mocksContainer/mockContainer.ts | 4 ++++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 954b24fd10b..af92c41b784 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1038,8 +1038,6 @@ export default class BattleScene extends SceneBase { const playerField = this.getPlayerField(); - this.newArena(Biome.VOLCANO); - if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) { battleConfig = this.gameMode.getFixedBattle(newWaveIndex); newDouble = battleConfig.double; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ef542971057..d4720ce3e47 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -34,7 +34,7 @@ export function doTrainerExclamation(scene: BattleScene) { const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim"); exclamationSprite.setName("exclamation"); scene.field.add(exclamationSprite); - scene.field.moveTo(exclamationSprite, scene.field.list.length - 1); + scene.field.moveTo(exclamationSprite, scene.field.getAll().length - 1); exclamationSprite.setVisible(true); exclamationSprite.setPosition(110, 68); scene.tweens.add({ @@ -475,7 +475,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel()); const partyMemberExp = []; - let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1; + let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1); if (participantIds?.length > 0) { if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) { diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index e8dcc369662..22dde79c50e 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -122,7 +122,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(blastoise.exp).toBe(expBefore + laprasSpecies.baseExp * defaultWave); + expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1)); }); it("should leave encounter without battle", async () => { @@ -171,7 +171,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(pidgeot.exp).toBe(expBefore + laprasBaseExp * wave); + expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1)); }); it("should leave encounter without battle", async () => { diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index 4f8a58f7251..ea06c9de326 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -154,6 +154,10 @@ export default class MockContainer { // Sends this Game Object to the back of its parent's display list. } + moveTo(obj) { + // Moves this Game Object to the given index in the list. + } + moveAbove(obj) { // Moves this Game Object to be above the given Game Object in the display list. } From 2fce6a2769ef2a8441fe97a21dc0911f8bb7245a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Thu, 18 Jul 2024 23:17:05 -0400 Subject: [PATCH 12/27] add skeleton tests to fiery fallout --- .../fiery-fallout-encounter.test.ts | 233 ++++++++++++++++++ .../encounters/lost-at-sea-encounter.test.ts | 4 +- 2 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts new file mode 100644 index 00000000000..0c53066e5ff --- /dev/null +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -0,0 +1,233 @@ +import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import { Biome } from "#app/enums/biome"; +import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; +import { Species } from "#app/enums/species"; +import GameManager from "#app/test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; + +const namespace = "mysteryEncounter:fieryFallout"; +/** Arcanine and Ninetails for 2 Fire types. Lapras for burnable mon. */ +const defaultParty = [Species.ARCANINE, Species.NINETALES, Species.LAPRAS]; +const defaultBiome = Biome.VOLCANO; +const defaultWave = 45; + +describe("Fiery Fallout - Mystery Encounter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + game.override.mysteryEncounterChance(100); + game.override.startingWave(defaultWave); + game.override.startingBiome(defaultBiome); + + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( + new Map([ + [Biome.SEA, [MysteryEncounterType.FIERY_FALLOUT]], + [Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], + ]) + ); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + it("should have the correct properties", async () => { + await game.runToMysteryEncounter(defaultParty); + + expect(FieryFalloutEncounter.encounterType).toBe(MysteryEncounterType.FIERY_FALLOUT); + expect(FieryFalloutEncounter.dialogue).toBeDefined(); + expect(FieryFalloutEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`); + expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`); + expect(FieryFalloutEncounter.options.length).toBe(3); + }); + + it("should not spawn outside of volcano biome", async () => { + game.override.startingBiome(Biome.MOUNTAIN); + await game.runToMysteryEncounter(); + + expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); + }); + + it("should not run below wave 41", async () => { + game.override.startingWave(38); + + await game.runToMysteryEncounter(); + + expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); + }); + + it("should not run above wave 179", async () => { + game.override.startingWave(181); + + await game.runToMysteryEncounter(); + + expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); + }); + + // it("should set the correct dialog tokens during initialization", () => { + // vi.spyOn(game.scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle); + // + // const { onInit } = FieryFalloutEncounter; + // + // expect(FieryFalloutEncounter.onInit).toBeDefined(); + // + // const onInitResult = onInit(game.scene); + // + // expect(FieryFalloutEncounter.dialogueTokens?.damagePercentage).toBe("25"); + // expect(FieryFalloutEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); + // expect(FieryFalloutEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]); + // expect(onInitResult).toBe(true); + // }); + + // describe("Option 1 - Fight 2 Volcarona", () => { + // it("should have the correct properties", () => { + // const option1 = LostAtSeaEncounter.options[0]; + // expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); + // expect(option1.dialogue).toBeDefined(); + // expect(option1.dialogue).toStrictEqual({ + // buttonLabel: `${namespace}:option:1:label`, + // disabledButtonLabel: `${namespace}:option:1:label_disabled`, + // buttonTooltip: `${namespace}:option:1:tooltip`, + // disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`, + // selected: [ + // { + // text: `${namespace}:option:1:selected`, + // }, + // ], + // }); + // }); + // + // it("should award exp to surfable PKM (Blastoise)", async () => { + // const laprasSpecies = getPokemonSpecies(Species.LAPRAS); + // + // await game.runToMysteryEncounter(defaultParty); + // const party = game.scene.getParty(); + // const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); + // const expBefore = blastoise.exp; + // + // await runSelectMysteryEncounterOption(game, 2); + // + // expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1)); + // }); + // + // it("should leave encounter without battle", async () => { + // game.override.startingWave(33); + // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + // + // await game.runToMysteryEncounter(defaultParty); + // await runSelectMysteryEncounterOption(game, 1); + // + // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + // }); + // + // it("should be disabled if no surfable PKM is in party", async () => { + // // TODO + // }); + // }); + + // describe("Option 2 - Suffer the weather", () => { + // it("should have the correct properties", () => { + // const option2 = LostAtSeaEncounter.options[1]; + // + // expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); + // expect(option2.dialogue).toBeDefined(); + // expect(option2.dialogue).toStrictEqual({ + // buttonLabel: `${namespace}:option:2:label`, + // disabledButtonLabel: `${namespace}:option:2:label_disabled`, + // buttonTooltip: `${namespace}:option:2:tooltip`, + // disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`, + // selected: [ + // { + // text: `${namespace}:option:2:selected`, + // }, + // ], + // }); + // }); + // + // it("should award exp to flyable PKM (Pidgeot)", async () => { + // const laprasBaseExp = 187; + // const wave = 33; + // game.override.startingWave(wave); + // + // await game.runToMysteryEncounter(defaultParty); + // const party = game.scene.getParty(); + // const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); + // const expBefore = pidgeot.exp; + // + // await runSelectMysteryEncounterOption(game, 2); + // + // expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1)); + // }); + // + // it("should leave encounter without battle", async () => { + // game.override.startingWave(33); + // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + // + // await game.runToMysteryEncounter(defaultParty); + // await runSelectMysteryEncounterOption(game, 2); + // + // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + // }); + // + // it("should be disabled if no flyable PKM is in party", async () => { + // // TODO + // }); + // }); + + // describe("Option 3 - use FIRE types", () => { + // it("should have the correct properties", () => { + // const option3 = LostAtSeaEncounter.options[2]; + // + // expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT); + // expect(option3.dialogue).toBeDefined(); + // expect(option3.dialogue).toStrictEqual({ + // buttonLabel: `${namespace}:option:3:label`, + // buttonTooltip: `${namespace}:option:3:tooltip`, + // selected: [ + // { + // text: `${namespace}:option:3:selected`, + // }, + // ], + // }); + // }); + // + // it("should damage all (allowed in battle) party PKM by 25%", async () => { + // game.override.startingWave(33); + // + // await game.runToMysteryEncounter(defaultParty); + // + // const party = game.scene.getParty(); + // const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + // vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); + // + // await runSelectMysteryEncounterOption(game, 3); + // + // const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); + // const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); + // allowedPkm.forEach((pkm) => + // expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) + // ); + // + // notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); + // }); + // + // it("should leave encounter without battle", async () => { + // game.override.startingWave(33); + // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + // + // await game.runToMysteryEncounter(defaultParty); + // await runSelectMysteryEncounterOption(game, 3); + // + // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + // }); + // }); +}); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 22dde79c50e..b614a400143 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -64,7 +64,7 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should not run below wave 11", async () => { - game.override.startingWave(10); + game.override.startingWave(9); await game.runToMysteryEncounter(); @@ -72,7 +72,7 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should not run above wave 179", async () => { - game.override.startingWave(180); + game.override.startingWave(181); await game.runToMysteryEncounter(); From 37419eb1d399972a0bac50020ca1c6c21c1fe03d Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 12:00:03 -0400 Subject: [PATCH 13/27] commit latest test changes --- src/battle.ts | 2 +- .../encounters/dark-deal-encounter.ts | 2 +- .../department-store-sale-encounter.ts | 2 +- .../encounters/field-trip-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 35 +- .../encounters/fight-or-flight-encounter.ts | 2 +- .../encounters/lost-at-sea-encounter.ts | 2 +- .../mysterious-challengers-encounter.ts | 2 +- .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/safari-zone-encounter.ts | 2 +- .../shady-vitamin-dealer-encounter.ts | 2 +- .../slumbering-snorlax-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 2 +- .../mystery-encounters/mystery-encounter.ts | 15 - .../utils/encounter-phase-utils.ts | 12 +- .../fiery-fallout-dialogue.ts | 6 +- src/phases.ts | 5 - src/phases/mystery-encounter-phases.ts | 2 +- .../mystery-encounter/encounterTestUtils.ts | 29 +- .../fiery-fallout-encounter.test.ts | 350 ++++++++++-------- src/test/utils/phaseInterceptor.ts | 3 +- 21 files changed, 269 insertions(+), 212 deletions(-) diff --git a/src/battle.ts b/src/battle.ts index ab47eac7993..9076eba7d0a 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -206,7 +206,7 @@ export default class Battle { getBgmOverride(scene: BattleScene): string { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_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()}`; } if (scene.musicPreference === 0) { diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 23ccba9c32e..698b374497b 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -71,7 +71,7 @@ const excludedBosses = [ /** * Dark Deal encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/61 | GitHub Issue #61} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const DarkDealEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL) diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 2e8a5ff67bd..131dc68aa14 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -18,7 +18,7 @@ const namespace = "mysteryEncounter:departmentStoreSale"; /** * Department Store Sale encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/33 | GitHub Issue #33} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const DepartmentStoreSaleEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 45ac52eb161..21db15ac7e0 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:fieldTrip"; /** * Field Trip encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/17 | GitHub Issue #17} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const FieldTripEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 2a7c26995d3..ae7d26feece 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -31,7 +31,7 @@ const DAMAGE_PERCENTAGE: number = 20; /** * Fiery Fallout encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const FieryFalloutEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT) @@ -127,8 +127,13 @@ export const FieryFalloutEncounter: IMysteryEncounter = async (scene: BattleScene) => { // Pick battle const encounter = scene.currentBattle.mysteryEncounter; - const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); + setEncounterRewards(scene, + { fillRemaining: true }, + null, + () => { + giveLeadPokemonCharcoal(scene); + }); + encounter.startOfBattleEffects.push( { sourceBattlerIndex: BattlerIndex.ENEMY, @@ -196,8 +201,8 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically - .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3PrimaryName dialogue token automatically + .withSecondaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3SecondaryName dialogue token automatically .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, @@ -215,8 +220,12 @@ export const FieryFalloutEncounter: IMysteryEncounter = // Fire types help calm the Volcarona const encounter = scene.currentBattle.mysteryEncounter; transitionMysteryEncounterIntroVisuals(scene); - const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); + setEncounterRewards(scene, + { fillRemaining: true }, + null, + () => { + giveLeadPokemonCharcoal(scene); + }); const primary = encounter.options[2].primaryPokemon; const secondary = encounter.options[2].secondaryPokemon[0]; @@ -227,3 +236,15 @@ export const FieryFalloutEncounter: IMysteryEncounter = .build() ) .build(); + +function giveLeadPokemonCharcoal(scene: BattleScene) { + // Give first party pokemon Charcoal for free at end of battle + const leadPokemon = scene.getParty()?.[0]; + if (leadPokemon) { + const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); + scene.addModifier(charcoal.type.newModifier(leadPokemon), true); + scene.updateModifiers(); + scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.name); + queueEncounterMessage(scene, `${namespace}:found_charcoal`); + } +} diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index ca64ba30552..2d142f46d4e 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -35,7 +35,7 @@ const namespace = "mysteryEncounter:fightOrFlight"; /** * Fight or Flight encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/24 | GitHub Issue #24} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const FightOrFlightEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index fb7d1bcbbb5..3418f977ccd 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -21,7 +21,7 @@ const namespace = "mysteryEncounter:lostAtSea"; /** * Lost at sea encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA) .withEncounterTier(MysteryEncounterTier.COMMON) diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index b1b61434511..382dc1f212e 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -26,7 +26,7 @@ const namespace = "mysteryEncounter:mysteriousChallengers"; /** * Mysterious Challengers encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/41 | GitHub Issue #41} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const MysteriousChallengersEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index a93df6cb264..620d5380754 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -14,7 +14,7 @@ const namespace = "mysteryEncounter:mysteriousChest"; /** * Mysterious Chest encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/32 | GitHub Issue #32} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 164ac112596..e5c5bd39b4d 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -24,7 +24,7 @@ const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768]; /** * Safari Zone encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/39 | GitHub Issue #39} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const SafariZoneEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 082eb9a1872..de20a1e11b0 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -17,7 +17,7 @@ const namespace = "mysteryEncounter:shadyVitaminDealer"; /** * Shady Vitamin Dealer encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/34 | GitHub Issue #34} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const ShadyVitaminDealerEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 2efae68c9eb..c905e0bd222 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -21,7 +21,7 @@ const namespace = "mysteryEncounter:slumberingSnorlax"; /** * Sleeping Snorlax encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/103 | GitHub Issue #103} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const SlumberingSnorlaxEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index e417bcc7ef4..2800a0befd1 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:trainingSession"; /** * Training Session encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/43 | GitHub Issue #43} - * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) + * @see For biome requirements check {@linkcode mysteryEncountersByBiome} */ export const TrainingSessionEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 289800d0b52..2162f30a994 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -71,8 +71,6 @@ export default interface IMysteryEncounter { onInit?: (scene: BattleScene) => boolean; /** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */ onVisualsStart?: (scene: BattleScene) => boolean; - /** Event right before MysteryEncounterPhase begins. Use for unshifting any phases before the actual encounter */ - onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; /** Will provide the player party EXP before rewards are displayed for that wave */ doEncounterExp?: (scene: BattleScene) => boolean; /** Will provide the player a rewards shop for that wave */ @@ -392,7 +390,6 @@ export class MysteryEncounterBuilder implements Partial { doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; onVisualsStart?: (scene: BattleScene) => boolean; - onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; @@ -625,18 +622,6 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { onInit: onInit }); } - /** - * Event callback right before MysteryEncounterPhase begins. - * Use for unshifting any last-minute phases before the actual encounter, as all phases are cleared and reset at that point. - * Example: set the weather before encounter begins - * - * @param onVisualsStart - synchronous callback function to perform immediately before MysteryEncounterPhase begins - * @returns - */ - withOnPreMysteryEncounterPhase(onPreMysteryEncounterPhase: (scene: BattleScene) => boolean): this & Required> { - return Object.assign(this, { onPreMysteryEncounterPhase: onPreMysteryEncounterPhase }); - } - /** * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in * diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index d4720ce3e47..c07307b0597 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -50,7 +50,7 @@ export function doTrainerExclamation(scene: BattleScene) { } }); - scene.playSound("GEN8- Exclaim.wav"); + scene.playSound("GEN8- Exclaim.wav", { volume: 0.8 }); } export interface EnemyPokemonConfig { @@ -417,10 +417,10 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p * Can have shop displayed or skipped * @param scene - Battle Scene * @param customShopRewards - adds a shop phase with the specified rewards / reward tiers - * @param nonShopRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop) + * @param nonShopPlayerItemRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop) * @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase) */ -export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) { +export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopPlayerItemRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) { scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => { if (preRewardsCallback) { preRewardsCallback(); @@ -432,8 +432,8 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust scene.tryRemovePhase(p => p instanceof SelectModifierPhase); } - if (nonShopRewards?.length > 0) { - nonShopRewards.forEach((reward) => { + if (nonShopPlayerItemRewards?.length > 0) { + nonShopPlayerItemRewards.forEach((reward) => { scene.unshiftPhase(new ModifierRewardPhase(scene, reward)); }); } else { @@ -679,7 +679,7 @@ export function handleEncounterStartOfBattleEffects(scene: BattleScene) { } else { source = scene.getEnemyField()[0]; } - scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.followUp)); + scene.pushPhase(new MovePhase(scene, source, effect.targets, effect.move, effect.followUp, effect.ignorePp)); }); // Pseudo turn end phase to reset flinch states, Endure, etc. diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts index 6f93cb239b1..2fbf5a15bda 100644 --- a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -8,7 +8,7 @@ export const fieryFalloutDialogue = { label: "Find the source", tooltip: "(?) Discover the source\n(-) Hard Battle", selected: `You push through the storm, and find two Volcarona in the middle of a mating dance! - $They don't take kindly to the interruption and attack!`, + $They don't take kindly to the interruption and attack!` }, 2: { label: "Hunker down", @@ -24,5 +24,7 @@ export const fieryFalloutDialogue = { selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance! $Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`, }, - } + }, + found_charcoal: `After the weather clears,\nyour {{leadPokemon}} spots something on the ground. + $@s{item_fanfare}{{leadPokemon}} gained a Charcoal!` }; diff --git a/src/phases.ts b/src/phases.ts index 2e6c8224de2..bbf3115e671 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1101,11 +1101,6 @@ export class EncounterPhase extends BattlePhase { this.scene.ui.clearText(); this.scene.ui.getMessageHandler().hideNameText(); - // Can add any additional unshift phases here before MysteryEncounterPhase begins (and all phase queues are cleared) - if (this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase) { - this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase(this.scene); - } - this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); this.end(); }; diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 1ac00cbea8e..25f6b140140 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -184,7 +184,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { pokemon.lapseTags(BattlerTagLapseType.TURN_END); }); - this.end(); + super.end(); } } diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 6caff2a8693..82975092a0f 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -1,15 +1,16 @@ import { Button } from "#app/enums/buttons"; -import { MessagePhase, VictoryPhase } from "#app/phases"; +import { CommandPhase, MessagePhase, VictoryPhase } from "#app/phases"; import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { Mode } from "#app/ui/ui"; import GameManager from "../utils/gameManager"; import MessageUiHandler from "#app/ui/message-ui-handler"; +import { Status, StatusEffect } from "#app/data/status-effect"; -export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number) { +export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) { // Handle any eventual queued messages (e.g. weather phase, etc.) game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { - const uiHandler = game.scene.ui.getHandler(); + const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); @@ -23,7 +24,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN uiHandler.processInput(Button.ACTION); }); - await game.phaseInterceptor.run(MysteryEncounterPhase); + await game.phaseInterceptor.to(MysteryEncounterPhase, true); // select the desired option const uiHandler = game.scene.ui.getHandler(); @@ -73,5 +74,23 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN uiHandler.processInput(Button.ACTION); }); - await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); + if (isBattle) { + await game.phaseInterceptor.to(CommandPhase); + } else { + await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); + } +} + +export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager) { + game.scene.clearPhaseQueue(); + game.scene.clearPhaseQueueSplice(); + game.scene.getEnemyParty().forEach(p => { + p.hp = 0; + p.status = new Status(StatusEffect.FAINT); + game.scene.field.remove(p); + }); + game.scene.unshiftPhase(new VictoryPhase(game.scene, 0)); + game.endPhase(); + game.phaseInterceptor.superEndPhase(); + await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, true); } diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 0c53066e5ff..a88a31968e2 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -5,6 +5,16 @@ import { Species } from "#app/enums/species"; import GameManager from "#app/test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; +import Battle from "#app/battle"; +import { Gender } from "#app/data/gender"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import * as BattleAnims from "#app/data/battle-anims"; +import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounterTestUtils"; +import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases"; +import { Moves } from "#enums/moves"; +import BattleScene from "#app/battle-scene"; +import { PokemonHeldItemModifier } from "#app/modifier/modifier"; const namespace = "mysteryEncounter:fieryFallout"; /** Arcanine and Ninetails for 2 Fire types. Lapras for burnable mon. */ @@ -15,6 +25,7 @@ const defaultWave = 45; describe("Fiery Fallout - Mystery Encounter", () => { let phaserGame: Phaser.Game; let game: GameManager; + let scene: BattleScene; beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); @@ -22,13 +33,14 @@ describe("Fiery Fallout - Mystery Encounter", () => { beforeEach(async () => { game = new GameManager(phaserGame); + scene = game.scene; game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ - [Biome.SEA, [MysteryEncounterType.FIERY_FALLOUT]], + [Biome.VOLCANO, [MysteryEncounterType.FIERY_FALLOUT]], [Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], ]) ); @@ -54,7 +66,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.startingBiome(Biome.MOUNTAIN); await game.runToMysteryEncounter(); - expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); + expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); }); it("should not run below wave 41", async () => { @@ -62,7 +74,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(); - expect(game.scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); + expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); }); it("should not run above wave 179", async () => { @@ -70,164 +82,186 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(); - expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined(); + expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); }); - // it("should set the correct dialog tokens during initialization", () => { - // vi.spyOn(game.scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle); - // - // const { onInit } = FieryFalloutEncounter; - // - // expect(FieryFalloutEncounter.onInit).toBeDefined(); - // - // const onInitResult = onInit(game.scene); - // - // expect(FieryFalloutEncounter.dialogueTokens?.damagePercentage).toBe("25"); - // expect(FieryFalloutEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]); - // expect(FieryFalloutEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]); - // expect(onInitResult).toBe(true); - // }); + it("should initialize fully ", async () => { + vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle); + const weatherSpy = vi.spyOn(scene.arena, "trySetWeather").mockReturnValue(true); + const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets"); + const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets"); - // describe("Option 1 - Fight 2 Volcarona", () => { - // it("should have the correct properties", () => { - // const option1 = LostAtSeaEncounter.options[0]; - // expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); - // expect(option1.dialogue).toBeDefined(); - // expect(option1.dialogue).toStrictEqual({ - // buttonLabel: `${namespace}:option:1:label`, - // disabledButtonLabel: `${namespace}:option:1:label_disabled`, - // buttonTooltip: `${namespace}:option:1:tooltip`, - // disabledButtonTooltip: `${namespace}:option:1:tooltip_disabled`, - // selected: [ - // { - // text: `${namespace}:option:1:selected`, - // }, - // ], - // }); - // }); - // - // it("should award exp to surfable PKM (Blastoise)", async () => { - // const laprasSpecies = getPokemonSpecies(Species.LAPRAS); - // - // await game.runToMysteryEncounter(defaultParty); - // const party = game.scene.getParty(); - // const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); - // const expBefore = blastoise.exp; - // - // await runSelectMysteryEncounterOption(game, 2); - // - // expect(blastoise.exp).toBe(expBefore + Math.floor(laprasSpecies.baseExp * defaultWave / 5 + 1)); - // }); - // - // it("should leave encounter without battle", async () => { - // game.override.startingWave(33); - // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - // - // await game.runToMysteryEncounter(defaultParty); - // await runSelectMysteryEncounterOption(game, 1); - // - // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); - // }); - // - // it("should be disabled if no surfable PKM is in party", async () => { - // // TODO - // }); - // }); + const { onInit } = FieryFalloutEncounter; - // describe("Option 2 - Suffer the weather", () => { - // it("should have the correct properties", () => { - // const option2 = LostAtSeaEncounter.options[1]; - // - // expect(option2.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); - // expect(option2.dialogue).toBeDefined(); - // expect(option2.dialogue).toStrictEqual({ - // buttonLabel: `${namespace}:option:2:label`, - // disabledButtonLabel: `${namespace}:option:2:label_disabled`, - // buttonTooltip: `${namespace}:option:2:tooltip`, - // disabledButtonTooltip: `${namespace}:option:2:tooltip_disabled`, - // selected: [ - // { - // text: `${namespace}:option:2:selected`, - // }, - // ], - // }); - // }); - // - // it("should award exp to flyable PKM (Pidgeot)", async () => { - // const laprasBaseExp = 187; - // const wave = 33; - // game.override.startingWave(wave); - // - // await game.runToMysteryEncounter(defaultParty); - // const party = game.scene.getParty(); - // const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); - // const expBefore = pidgeot.exp; - // - // await runSelectMysteryEncounterOption(game, 2); - // - // expect(pidgeot.exp).toBe(expBefore + Math.floor(laprasBaseExp * defaultWave / 5 + 1)); - // }); - // - // it("should leave encounter without battle", async () => { - // game.override.startingWave(33); - // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - // - // await game.runToMysteryEncounter(defaultParty); - // await runSelectMysteryEncounterOption(game, 2); - // - // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); - // }); - // - // it("should be disabled if no flyable PKM is in party", async () => { - // // TODO - // }); - // }); + expect(FieryFalloutEncounter.onInit).toBeDefined(); - // describe("Option 3 - use FIRE types", () => { - // it("should have the correct properties", () => { - // const option3 = LostAtSeaEncounter.options[2]; - // - // expect(option3.optionMode).toBe(EncounterOptionMode.DEFAULT); - // expect(option3.dialogue).toBeDefined(); - // expect(option3.dialogue).toStrictEqual({ - // buttonLabel: `${namespace}:option:3:label`, - // buttonTooltip: `${namespace}:option:3:tooltip`, - // selected: [ - // { - // text: `${namespace}:option:3:selected`, - // }, - // ], - // }); - // }); - // - // it("should damage all (allowed in battle) party PKM by 25%", async () => { - // game.override.startingWave(33); - // - // await game.runToMysteryEncounter(defaultParty); - // - // const party = game.scene.getParty(); - // const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); - // vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); - // - // await runSelectMysteryEncounterOption(game, 3); - // - // const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); - // const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); - // allowedPkm.forEach((pkm) => - // expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) - // ); - // - // notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); - // }); - // - // it("should leave encounter without battle", async () => { - // game.override.startingWave(33); - // const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - // - // await game.runToMysteryEncounter(defaultParty); - // await runSelectMysteryEncounterOption(game, 3); - // - // expect(leaveEncounterWithoutBattleSpy).toBeCalled(); - // }); - // }); + const onInitResult = onInit(scene); + + expect(FieryFalloutEncounter.enemyPartyConfigs).toEqual([ + { + pokemonConfigs: [ + { + species: getPokemonSpecies(Species.VOLCARONA), + isBoss: false, + gender: Gender.MALE + }, + { + species: getPokemonSpecies(Species.VOLCARONA), + isBoss: false, + gender: Gender.FEMALE + } + ], + doubleBattle: true, + disableSwitch: true + } + ]); + expect(weatherSpy).toHaveBeenCalledTimes(1); + await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled()); + await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled()); + expect(onInitResult).toBe(true); + }); + + describe("Option 1 - Fight 2 Volcarona", () => { + it("should have the correct properties", () => { + const option1 = FieryFalloutEncounter.options[0]; + expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option1.dialogue).toBeDefined(); + expect(option1.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, + selected: [ + { + text: `${namespace}:option:1:selected`, + }, + ], + }); + }); + + it("should start battle against 2 Volcarona", async () => { + const phaseSpy = vi.spyOn(scene, "pushPhase"); + + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 1, true); + + const enemyField = scene.getEnemyField(); + expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(enemyField.length).toBe(2); + expect(enemyField[0].species.speciesId).toBe(Species.VOLCARONA); + expect(enemyField[1].species.speciesId).toBe(Species.VOLCARONA); + expect(enemyField[0].gender).not.toEqual(enemyField[1].gender); // Should be opposite gender + + const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]); + expect(movePhases.length).toBe(4); + expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.FIRE_SPIN).length).toBe(2); // Fire spin used twice before battle + expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.QUIVER_DANCE).length).toBe(2); // Quiver Dance used twice before battle + }); + + it("should give charcoal to lead pokemon", async () => { + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 1, true); + await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + + const leadPokemonId = scene.getParty()?.[0].id; + const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier + && (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[]; + const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal"); + expect(charcoal).toBeDefined; + }); + }); + + describe("Option 2 - Suffer the weather", () => { + it("should have the correct properties", () => { + const option1 = FieryFalloutEncounter.options[0]; + expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option1.dialogue).toBeDefined(); + expect(option1.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, + selected: [ + { + text: `${namespace}:option:2:selected`, + }, + ], + }); + }); + + it("should damage all (allowed in battle) party PKM by 25%", async () => { + game.override.startingWave(33); + + await game.runToMysteryEncounter(defaultParty); + + const party = scene.getParty(); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); + + await runSelectMysteryEncounterOption(game, 3); + + const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); + const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); + allowedPkm.forEach((pkm) => + expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) + ); + + notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); + }); + + it("should leave encounter without battle", async () => { + game.override.startingWave(33); + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 3); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); + + describe("Option 3 - use FIRE types", () => { + it("should have the correct properties", () => { + const option1 = FieryFalloutEncounter.options[0]; + expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option1.dialogue).toBeDefined(); + expect(option1.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, + selected: [ + { + text: `${namespace}:option:1:selected`, + }, + ], + }); + }); + + it("should damage all (allowed in battle) party PKM by 25%", async () => { + game.override.startingWave(33); + + await game.runToMysteryEncounter(defaultParty); + + const party = scene.getParty(); + const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); + vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); + + await runSelectMysteryEncounterOption(game, 3); + + const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); + const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); + allowedPkm.forEach((pkm) => + expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) + ); + + notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); + }); + + it("should leave encounter without battle", async () => { + game.override.startingWave(33); + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(defaultParty); + await runSelectMysteryEncounterOption(game, 3); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); }); diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index 39278c673d4..7114d63afa0 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -104,7 +104,8 @@ export default class PhaseInterceptor { [MysteryEncounterBattlePhase, this.startPhase], [MysteryEncounterRewardsPhase, this.startPhase], [PostMysteryEncounterPhase, this.startPhase], - [LearnMovePhase, this.startPhase] + [LearnMovePhase, this.startPhase], + // [CommonAnimPhase, this.startPhase] ]; private endBySetMode = [ From 03f999e16973464e3e42ec348659f1df1d28012b Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 14:51:38 -0400 Subject: [PATCH 14/27] finish unit tests for fiery fallout --- src/battle-scene.ts | 2 +- src/data/battle-anims.ts | 6 +- .../encounters/fiery-fallout-encounter.ts | 6 +- .../mystery-encounter-requirements.ts | 98 +++---------------- .../mystery-encounters/mystery-encounter.ts | 4 +- .../utils/encounter-phase-utils.ts | 9 +- .../utils/encounter-pokemon-utils.ts | 4 +- .../mystery-encounter/encounterTestUtils.ts | 3 +- .../fiery-fallout-encounter.test.ts | 74 +++++++------- .../encounters/lost-at-sea-encounter.test.ts | 1 + .../mystery-encounter.test.ts | 1 + src/test/utils/overridesHelper.ts | 11 +++ 12 files changed, 80 insertions(+), 139 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index af92c41b784..9c32c58efdb 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2650,7 +2650,7 @@ export default class BattleScene extends SceneBase { return encounter; } - // Common / Uncommon / Rare / Super Rare + // Common / Great / Ultra / Rogue const tierWeights = [64, 40, 21, 3]; // Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 81536d74771..3f00a012ee3 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -528,10 +528,10 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise { /** * Fetches animation configs to be used in a Mystery Encounter * @param scene - * @param anims - one or more animations to fetch + * @param encounterAnim - one or more animations to fetch */ -export async function initEncounterAnims(scene: BattleScene, anims: EncounterAnim | EncounterAnim[]): Promise { - anims = anims instanceof Array ? anims : [anims]; +export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise { + const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim]; const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimIds = Utils.getEnumValues(EncounterAnim); const encounterAnimFetches = []; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index ae7d26feece..705ba3296a4 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -175,7 +175,7 @@ export const FieryFalloutEncounter: IMysteryEncounter = async (scene: BattleScene) => { // Damage non-fire types and burn 1 random non-fire type member const encounter = scene.currentBattle.mysteryEncounter; - const nonFireTypes = scene.getParty().filter((p) => !p.getTypes().includes(Type.FIRE)); + const nonFireTypes = scene.getParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE)); for (const pkm of nonFireTypes) { const percentage = DAMAGE_PERCENTAGE / 100; @@ -201,8 +201,8 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3PrimaryName dialogue token automatically - .withSecondaryPokemonRequirement(new TypeRequirement(Type.STEEL, true,1)) // Will set option3SecondaryName dialogue token automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically + .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index 6ec5d652fb4..ac1d49c919e 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -103,12 +103,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement { constructor(timeOfDay: TimeOfDay | TimeOfDay[]) { super(); - if (timeOfDay instanceof Array) { - this.requiredTimeOfDay = timeOfDay; - } else { - this.requiredTimeOfDay = []; - this.requiredTimeOfDay.push(timeOfDay); - } + this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay]; } meetsRequirement(scene: BattleScene): boolean { @@ -130,12 +125,7 @@ export class WeatherRequirement extends EncounterSceneRequirement { constructor(weather: WeatherType | WeatherType[]) { super(); - if (weather instanceof Array) { - this.requiredWeather = weather; - } else { - this.requiredWeather = []; - this.requiredWeather.push(weather); - } + this.requiredWeather = Array.isArray(weather) ? weather : [weather]; } meetsRequirement(scene: BattleScene): boolean { @@ -185,12 +175,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement { requiredItems?: ModifierType[]; // TODO: not implemented constructor(item: ModifierType | ModifierType[]) { super(); - if (item instanceof Array) { - this.requiredItems = item; - } else { - this.requiredItems = []; - this.requiredItems.push(item); - } + this.requiredItems = Array.isArray(item) ? item : [item]; } meetsRequirement(scene: BattleScene): boolean { @@ -251,12 +236,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (species instanceof Array) { - this.requiredSpecies = species; - } else { - this.requiredSpecies = []; - this.requiredSpecies.push(species); - } + this.requiredSpecies = Array.isArray(species) ? species : [species]; } meetsRequirement(scene: BattleScene): boolean { @@ -294,12 +274,7 @@ export class NatureRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (nature instanceof Array) { - this.requiredNature = nature; - } else { - this.requiredNature = []; - this.requiredNature.push(nature); - } + this.requiredNature = Array.isArray(nature) ? nature : [nature]; } meetsRequirement(scene: BattleScene): boolean { @@ -338,12 +313,7 @@ export class TypeRequirement extends EncounterPokemonRequirement { this.excludeFainted = excludeFainted; this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (type instanceof Array) { - this.requiredType = type; - } else { - this.requiredType = []; - this.requiredType.push(type); - } + this.requiredType = Array.isArray(type) ? type : [type]; } meetsRequirement(scene: BattleScene): boolean { @@ -388,12 +358,7 @@ export class MoveRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (moves instanceof Array) { - this.requiredMoves = moves; - } else { - this.requiredMoves = []; - this.requiredMoves.push(moves); - } + this.requiredMoves = Array.isArray(moves) ? moves : [moves]; } meetsRequirement(scene: BattleScene): boolean { @@ -437,12 +402,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (learnableMove instanceof Array) { - this.requiredMoves = learnableMove; - } else { - this.requiredMoves = []; - this.requiredMoves.push(learnableMove); - } + this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove]; } meetsRequirement(scene: BattleScene): boolean { @@ -482,12 +442,7 @@ export class EvolutionTargetSpeciesRequirement extends EncounterPokemonRequireme super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (evolutionTargetSpecies instanceof Array) { - this.requiredEvolutionTargetSpecies = evolutionTargetSpecies; - } else { - this.requiredEvolutionTargetSpecies = []; - this.requiredEvolutionTargetSpecies.push(evolutionTargetSpecies); - } + this.requiredEvolutionTargetSpecies = Array.isArray(evolutionTargetSpecies) ? evolutionTargetSpecies : [evolutionTargetSpecies]; } meetsRequirement(scene: BattleScene): boolean { @@ -526,12 +481,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (abilities instanceof Array) { - this.requiredAbilities = abilities; - } else { - this.requiredAbilities = []; - this.requiredAbilities.push(abilities); - } + this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities]; } meetsRequirement(scene: BattleScene): boolean { @@ -571,12 +521,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (statusEffect instanceof Array) { - this.requiredStatusEffect = statusEffect; - } else { - this.requiredStatusEffect = []; - this.requiredStatusEffect.push(statusEffect); - } + this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect]; } meetsRequirement(scene: BattleScene): boolean { @@ -646,12 +591,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (formChangeItem instanceof Array) { - this.requiredFormChangeItem = formChangeItem; - } else { - this.requiredFormChangeItem = []; - this.requiredFormChangeItem.push(formChangeItem); - } + this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem]; } meetsRequirement(scene: BattleScene): boolean { @@ -703,12 +643,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (evolutionItems instanceof Array) { - this.requiredEvolutionItem = evolutionItems; - } else { - this.requiredEvolutionItem = []; - this.requiredEvolutionItem.push(evolutionItems); - } + this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems]; } meetsRequirement(scene: BattleScene): boolean { @@ -757,12 +692,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement { super(); this.minNumberOfPokemon = minNumberOfPokemon; this.invertQuery = invertQuery; - if (heldItem instanceof Array) { - this.requiredHeldItemModifier = heldItem; - } else { - this.requiredHeldItemModifier = []; - this.requiredHeldItemModifier.push(heldItem); - } + this.requiredHeldItemModifier = Array.isArray(heldItem) ? heldItem : [heldItem]; } meetsRequirement(scene: BattleScene): boolean { diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 2162f30a994..7fc66dfa4e7 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -489,8 +489,8 @@ export class MysteryEncounterBuilder implements Partial { * @returns */ withAnimations(...encounterAnimations: EncounterAnim[]): this & Required> { - encounterAnimations = encounterAnimations instanceof Array ? encounterAnimations : [encounterAnimations]; - return Object.assign(this, { encounterAnimations: encounterAnimations }); + const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations]; + return Object.assign(this, { encounterAnimations: animations }); } /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c07307b0597..9c569775f38 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -207,10 +207,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: } // Set Status - if (partyConfig.pokemonConfigs[e].status) { + const statusEffects = partyConfig.pokemonConfigs[e].status; + if (statusEffects) { // Default to cureturn 3 for sleep - const status = partyConfig.pokemonConfigs[e].status instanceof Array ? partyConfig.pokemonConfigs[e].status[0] : partyConfig.pokemonConfigs[e].status; - const cureTurn = partyConfig.pokemonConfigs[e].status instanceof Array ? partyConfig.pokemonConfigs[e].status[1] : partyConfig.pokemonConfigs[e].status === StatusEffect.SLEEP ? 3 : null; + const status = Array.isArray(statusEffects) ? statusEffects[0] : statusEffects; + const cureTurn = Array.isArray(statusEffects) ? statusEffects[1] : statusEffects === StatusEffect.SLEEP ? 3 : null; enemyPokemon.status = new Status(status, 0, cureTurn); } @@ -281,7 +282,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: * @param moves */ export function initCustomMovesForEncounter(scene: BattleScene, moves: Moves | Moves[]) { - moves = moves instanceof Array ? moves : [moves]; + moves = Array.isArray(moves) ? moves : [moves]; return Promise.all(moves.map(move => initMoveAnim(scene, move))) .then(() => loadMoveAnimAssets(scene, moves)); } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index b505c4f971d..76c6cb9c680 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -98,8 +98,8 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, unfainted: boole * @returns */ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species { - let min = starterTiers instanceof Array ? starterTiers[0] : starterTiers; - let max = starterTiers instanceof Array ? starterTiers[1] : starterTiers; + let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers; + let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers; let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters) .map(s => [parseInt(s) as Species, speciesStarters[s] as number]) diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 82975092a0f..218f2cdb971 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -89,8 +89,7 @@ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManage p.status = new Status(StatusEffect.FAINT); game.scene.field.remove(p); }); - game.scene.unshiftPhase(new VictoryPhase(game.scene, 0)); - game.endPhase(); + game.scene.pushPhase(new VictoryPhase(game.scene, 0)); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, true); } diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index a88a31968e2..19d909cab64 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -9,16 +9,19 @@ import Battle from "#app/battle"; import { Gender } from "#app/data/gender"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import * as BattleAnims from "#app/data/battle-anims"; +import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; import { runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounterTestUtils"; import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases"; import { Moves } from "#enums/moves"; import BattleScene from "#app/battle-scene"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; +import { Type } from "#app/data/type"; +import { Status, StatusEffect } from "#app/data/status-effect"; const namespace = "mysteryEncounter:fieryFallout"; -/** Arcanine and Ninetails for 2 Fire types. Lapras for burnable mon. */ -const defaultParty = [Species.ARCANINE, Species.NINETALES, Species.LAPRAS]; +/** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */ +const defaultParty = [Species.ARCANINE, Species.NINETALES, Species.LAPRAS, Species.GENGAR, Species.ABRA]; const defaultBiome = Biome.VOLCANO; const defaultWave = 45; @@ -37,6 +40,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); + game.override.trainerWave(false); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -168,12 +172,12 @@ describe("Fiery Fallout - Mystery Encounter", () => { && (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[]; const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal"); expect(charcoal).toBeDefined; - }); + }, 100000000); }); describe("Option 2 - Suffer the weather", () => { it("should have the correct properties", () => { - const option1 = FieryFalloutEncounter.options[0]; + const option1 = FieryFalloutEncounter.options[1]; expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); expect(option1.dialogue).toBeDefined(); expect(option1.dialogue).toStrictEqual({ @@ -187,32 +191,32 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); }); - it("should damage all (allowed in battle) party PKM by 25%", async () => { - game.override.startingWave(33); - + it("should damage all non-fire party PKM by 20% and randomly burn 1", async () => { await game.runToMysteryEncounter(defaultParty); const party = scene.getParty(); + const lapras = party.find((pkm) => pkm.species.speciesId === Species.LAPRAS); + lapras.status = new Status(StatusEffect.POISON); const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); - await runSelectMysteryEncounterOption(game, 3); + await runSelectMysteryEncounterOption(game, 2); - const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); - const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); - allowedPkm.forEach((pkm) => - expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) - ); - - notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); + const burnablePokemon = party.filter((pkm) => pkm.isAllowedInBattle() && !pkm.getTypes().includes(Type.FIRE)); + const notBurnablePokemon = party.filter((pkm) => !pkm.isAllowedInBattle() || pkm.getTypes().includes(Type.FIRE)); + expect(scene.currentBattle.mysteryEncounter.dialogueTokens["burnedPokemon"]).toBe("Gengar"); + burnablePokemon.forEach((pkm) => { + expect(pkm.hp, `${pkm.name} should have received 20% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.2)); + }); + expect(burnablePokemon.some(pkm => pkm?.status?.effect === StatusEffect.BURN)).toBeTruthy(); + notBurnablePokemon.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); }); it("should leave encounter without battle", async () => { - game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); await game.runToMysteryEncounter(defaultParty); - await runSelectMysteryEncounterOption(game, 3); + await runSelectMysteryEncounterOption(game, 2); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); }); @@ -220,42 +224,36 @@ describe("Fiery Fallout - Mystery Encounter", () => { describe("Option 3 - use FIRE types", () => { it("should have the correct properties", () => { - const option1 = FieryFalloutEncounter.options[0]; - expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); + const option1 = FieryFalloutEncounter.options[2]; + expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_SPECIAL); expect(option1.dialogue).toBeDefined(); expect(option1.dialogue).toStrictEqual({ - buttonLabel: `${namespace}:option:1:label`, - buttonTooltip: `${namespace}:option:1:tooltip`, + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + disabledButtonTooltip: `${namespace}:option:3:disabled_tooltip`, selected: [ { - text: `${namespace}:option:1:selected`, + text: `${namespace}:option:3:selected`, }, ], }); }); - it("should damage all (allowed in battle) party PKM by 25%", async () => { - game.override.startingWave(33); - + it("should give charcoal to lead pokemon", async () => { await game.runToMysteryEncounter(defaultParty); - - const party = scene.getParty(); - const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); - vi.spyOn(abra, "isAllowedInBattle").mockReturnValue(false); - await runSelectMysteryEncounterOption(game, 3); + // await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); - const allowedPkm = party.filter((pkm) => pkm.isAllowedInBattle()); - const notAllowedPkm = party.filter((pkm) => !pkm.isAllowedInBattle()); - allowedPkm.forEach((pkm) => - expect(pkm.hp, `${pkm.name} should have receivd 25% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.25)) - ); - - notAllowedPkm.forEach((pkm) => expect(pkm.hp, `${pkm.name} should be full hp: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp())); + const leadPokemonId = scene.getParty()?.[0].id; + const leadPokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier + && (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[]; + const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal"); + expect(charcoal).toBeDefined; }); it("should leave encounter without battle", async () => { - game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); await game.runToMysteryEncounter(defaultParty); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index b614a400143..99b1c8b4764 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -31,6 +31,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); + game.override.trainerWave(false); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index b840302278e..cadca89b5e8 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -24,6 +24,7 @@ describe("Mystery Encounters", () => { game.override.startingWave(11); game.override.mysteryEncounterChance(100); game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); + game.override.trainerWave(false); }); it("Spawns a mystery encounter", async () => { diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 85d458bf826..97caeafd7a6 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -60,6 +60,17 @@ export class OverridesHelper { return spy; } + /** + * Override each wave to have or not have standard trainer battles + * @returns spy instance + * @param isTrainer + */ + trainerWave(isTrainer: boolean): MockInstance { + const spy = vi.spyOn(this.game.scene.gameMode, "isWaveTrainer").mockReturnValue(isTrainer); + this.log(`${isTrainer? "forcing" : "ignoring"} trainer waves!`); + return spy; + } + /** * Override the weather (type) * @param type weather type to set From 670013a1076562698db6220777577d65772d0c4f Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 15:33:07 -0400 Subject: [PATCH 15/27] bug fix for empty modifier shop --- src/phases.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index bbf3115e671..22a7b8bdd98 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -5386,10 +5386,10 @@ export class SelectModifierPhase extends BattlePhase { return true; case 1: if (typeOptions.length === 0) { - this.scene.ui.revertMode(); + this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); super.end(); - return; + return true; } modifierType = typeOptions[cursor].type; break; From fc7d7092029aa465c41a5f85158bc860b0cd9cd5 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:24:21 -0400 Subject: [PATCH 16/27] Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- .../mystery-encounters/encounters/fiery-fallout-encounter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 705ba3296a4..96a2fad1b15 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -36,7 +36,7 @@ const DAMAGE_PERCENTAGE: number = 20; export const FieryFalloutEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT) .withEncounterTier(MysteryEncounterTier.COMMON) - .withSceneWaveRangeRequirement(40, 180) // waves 10 to 180 + .withSceneWaveRangeRequirement(40, 180) .withCatchAllowed(true) .withIntroSpriteConfigs([]) // Set in onInit() .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) From d2874e29ac2c56a071608f9939423783267fc26a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:24:33 -0400 Subject: [PATCH 17/27] Update src/test/utils/overridesHelper.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/test/utils/overridesHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 97caeafd7a6..15a8cc9aa99 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -54,7 +54,7 @@ export class OverridesHelper { * @param wave the wave (index) to set. Classic: `1`-`200` * @returns spy instance */ - startingWave(wave: number): MockInstance { + startingWave(wave: number) { const spy = vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(wave); this.log(`Starting wave set to ${wave}!`); return spy; From 1e7c3b575fa3f699c147390d298319a5965834b6 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:24:43 -0400 Subject: [PATCH 18/27] Update src/test/utils/overridesHelper.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/test/utils/overridesHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 15a8cc9aa99..3c341808036 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -76,7 +76,7 @@ export class OverridesHelper { * @param type weather type to set * @returns spy instance */ - weather(type: WeatherType): MockInstance { + weather(type: WeatherType) { const spy = vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type); this.log(`Weather set to ${Weather[type]} (=${type})!`); return spy; From 6a697b193eafe9cc37eff818170da01428c2e3e7 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:24:52 -0400 Subject: [PATCH 19/27] Update src/test/utils/overridesHelper.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/test/utils/overridesHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 3c341808036..52f53d697ad 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -87,7 +87,7 @@ export class OverridesHelper { * @param seed the seed to set * @returns spy instance */ - seed(seed: string): MockInstance { + seed(seed: string) { const spy = vi.spyOn(this.game.scene, "resetSeed").mockImplementation(() => { this.game.scene.waveSeed = seed; Phaser.Math.RND.sow([seed]); From f80901f7b94e76f187eb671c85862538a0e15c0f Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:25:03 -0400 Subject: [PATCH 20/27] Update src/test/utils/overridesHelper.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/test/utils/overridesHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 52f53d697ad..2b60813ef53 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -20,7 +20,7 @@ export class OverridesHelper { * @param percentage the encounter chance in % * @returns spy instance */ - mysteryEncounterChance(percentage: number): MockInstance { + mysteryEncounterChance(percentage: number) { const maxRate: number = 256; // 100% const rate = maxRate * (percentage / 100); const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(rate); From 12cd014e1163ad318691cbf13615ed9b05393c5a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:26:18 -0400 Subject: [PATCH 21/27] Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- .../mystery-encounters/encounters/fiery-fallout-encounter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 96a2fad1b15..b99a7092902 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -186,7 +186,8 @@ export const FieryFalloutEncounter: IMysteryEncounter = // Burn random member const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status?.effect === StatusEffect.BURN); if (burnable?.length > 0) { - const chosenPokemon = burnable[randSeedInt(burnable.length - 1)]; + const roll = randSeedInt(burnable.length); + const chosenPokemon = burnable[roll]; if (chosenPokemon.trySetStatus(StatusEffect.BURN)) { // Burn applied encounter.setDialogueToken("burnedPokemon", chosenPokemon.name); From 9bcaf6a336a4ef4be95fc70f364ec820d63f115e Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:26:40 -0400 Subject: [PATCH 22/27] Update src/data/battle-anims.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/data/battle-anims.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 3f00a012ee3..6f2f293d099 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -1038,8 +1038,7 @@ export abstract class BattleAnim { let t = 0; for (const frame of frames) { - let x = frame.x; - let y = frame.y; + let { x , y } = frame; const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1); const scaleY = (frame.zoomY / 100); x += targetInitialX; From 186a1af7b82a0da8102a2e4eb3e05b29682ef662 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 19:21:46 -0400 Subject: [PATCH 23/27] nit updates and cleanup --- src/battle-scene.ts | 4 +-- src/battle.ts | 2 +- .../mystery-encounters/mystery-encounter.ts | 16 ++++++---- .../fiery-fallout-encounter.test.ts | 32 ++++++++++++++----- .../encounters/lost-at-sea-encounter.test.ts | 31 +++++++++++++----- .../mystery-encounter.test.ts | 12 ++----- .../phases/mystery-encounter-phase.test.ts | 15 ++------- src/test/utils/gameManager.ts | 8 ++++- src/test/utils/overridesHelper.ts | 20 +++++++++--- 9 files changed, 89 insertions(+), 51 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 9c32c58efdb..4c0f31b051e 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2650,8 +2650,8 @@ export default class BattleScene extends SceneBase { return encounter; } - // Common / Great / Ultra / Rogue - const tierWeights = [64, 40, 21, 3]; + // See Enum values for base tier weights + const tierWeights = [MysteryEncounterTier.COMMON, MysteryEncounterTier.GREAT, MysteryEncounterTier.ULTRA, MysteryEncounterTier.ROGUE]; // Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run this.mysteryEncounterData.encounteredEvents.forEach(val => { diff --git a/src/battle.ts b/src/battle.ts index 9076eba7d0a..2c033ce38bf 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -206,7 +206,7 @@ export default class Battle { getBgmOverride(scene: BattleScene): string { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterVariant === MysteryEncounterVariant.TRAINER_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()}`; } if (scene.musicPreference === 0) { diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 7fc66dfa4e7..6136e338854 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -27,15 +27,19 @@ export enum MysteryEncounterVariant { WILD_BATTLE, BOSS_BATTLE, NO_BATTLE, + /** For spawning new encounter queries instead of continuing to next wave */ REPEATED_ENCOUNTER } +/** + * Enum values are base spawn weights of each tier + */ export enum MysteryEncounterTier { - COMMON, - GREAT, - ULTRA, - ROGUE, - MASTER // Not currently used + COMMON = 64, + GREAT = 40, + ULTRA = 21, + ROGUE = 3, + MASTER = 0 // Not currently used } export interface StartOfBattleEffect { @@ -438,7 +442,7 @@ export class MysteryEncounterBuilder implements Partial { * @param callback - {@linkcode OptionPhaseCallback} * @returns */ - withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback) { + withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this { return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build()); } diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 19d909cab64..9bcb3a36d2c 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -18,6 +18,7 @@ import BattleScene from "#app/battle-scene"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { Type } from "#app/data/type"; import { Status, StatusEffect } from "#app/data/status-effect"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; const namespace = "mysteryEncounter:fieryFallout"; /** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */ @@ -38,9 +39,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { game = new GameManager(phaserGame); scene = game.scene; game.override.mysteryEncounterChance(100); + game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.trainerWave(false); + game.override.disableTrainerWave(true); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -55,9 +57,11 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - await game.runToMysteryEncounter(defaultParty); + game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); expect(FieryFalloutEncounter.encounterType).toBe(MysteryEncounterType.FIERY_FALLOUT); + expect(FieryFalloutEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(FieryFalloutEncounter.dialogue).toBeDefined(); expect(FieryFalloutEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); expect(FieryFalloutEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); @@ -126,6 +130,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); describe("Option 1 - Fight 2 Volcarona", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT); + }); + it("should have the correct properties", () => { const option1 = FieryFalloutEncounter.options[0]; expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); @@ -144,7 +152,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { it("should start battle against 2 Volcarona", async () => { const phaseSpy = vi.spyOn(scene, "pushPhase"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runSelectMysteryEncounterOption(game, 1, true); const enemyField = scene.getEnemyField(); @@ -161,7 +169,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should give charcoal to lead pokemon", async () => { - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runSelectMysteryEncounterOption(game, 1, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); @@ -176,6 +184,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); describe("Option 2 - Suffer the weather", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT); + }); + it("should have the correct properties", () => { const option1 = FieryFalloutEncounter.options[1]; expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT); @@ -192,7 +204,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should damage all non-fire party PKM by 20% and randomly burn 1", async () => { - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); const party = scene.getParty(); const lapras = party.find((pkm) => pkm.species.speciesId === Species.LAPRAS); @@ -215,7 +227,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { it("should leave encounter without battle", async () => { const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runSelectMysteryEncounterOption(game, 2); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); @@ -223,6 +235,10 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); describe("Option 3 - use FIRE types", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT); + }); + it("should have the correct properties", () => { const option1 = FieryFalloutEncounter.options[2]; expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_SPECIAL); @@ -240,7 +256,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should give charcoal to lead pokemon", async () => { - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runSelectMysteryEncounterOption(game, 3); // await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); @@ -256,7 +272,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { it("should leave encounter without battle", async () => { const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runSelectMysteryEncounterOption(game, 3); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 99b1c8b4764..04854574a77 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -11,6 +11,7 @@ import { Species } from "#app/enums/species"; import GameManager from "#app/test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runSelectMysteryEncounterOption } from "../encounterTestUtils"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; const namespace = "mysteryEncounter:lostAtSea"; /** Blastoise for surf. Pidgeot for fly. Abra for none. */ @@ -31,7 +32,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.trainerWave(false); + game.override.disableTrainerWave(true); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -46,9 +47,11 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - await game.runToMysteryEncounter(defaultParty); + game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); + expect(LostAtSeaEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(LostAtSeaEncounter.dialogue).toBeDefined(); expect(LostAtSeaEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); expect(LostAtSeaEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); @@ -96,6 +99,10 @@ describe("Lost at Sea - Mystery Encounter", () => { }); describe("Option 1 - Surf", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA); + }); + it("should have the correct properties", () => { const option1 = LostAtSeaEncounter.options[0]; expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT); @@ -116,7 +123,7 @@ describe("Lost at Sea - Mystery Encounter", () => { it("should award exp to surfable PKM (Blastoise)", async () => { const laprasSpecies = getPokemonSpecies(Species.LAPRAS); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); const blastoise = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); const expBefore = blastoise.exp; @@ -130,7 +137,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); await runSelectMysteryEncounterOption(game, 1); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); @@ -142,6 +149,10 @@ describe("Lost at Sea - Mystery Encounter", () => { }); describe("Option 2 - Fly", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA); + }); + it("should have the correct properties", () => { const option2 = LostAtSeaEncounter.options[1]; @@ -165,7 +176,7 @@ describe("Lost at Sea - Mystery Encounter", () => { const wave = 33; game.override.startingWave(wave); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); const pidgeot = party.find((pkm) => pkm.species.speciesId === Species.PIDGEOT); const expBefore = pidgeot.exp; @@ -179,7 +190,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); await runSelectMysteryEncounterOption(game, 2); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); @@ -191,6 +202,10 @@ describe("Lost at Sea - Mystery Encounter", () => { }); describe("Option 3 - Wander aimlessy", () => { + beforeEach(async () => { + game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA); + }); + it("should have the correct properties", () => { const option3 = LostAtSeaEncounter.options[2]; @@ -210,7 +225,7 @@ describe("Lost at Sea - Mystery Encounter", () => { it("should damage all (allowed in battle) party PKM by 25%", async () => { game.override.startingWave(33); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); const party = game.scene.getParty(); const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA); @@ -231,7 +246,7 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.startingWave(33); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); - await game.runToMysteryEncounter(defaultParty); + await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); await runSelectMysteryEncounterOption(game, 3); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index cadca89b5e8..ef0b5b3238a 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -24,24 +24,18 @@ describe("Mystery Encounters", () => { game.override.startingWave(11); game.override.mysteryEncounterChance(100); game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); - game.override.trainerWave(false); + game.override.disableTrainerWave(true); }); it("Spawns a mystery encounter", async () => { - await game.runToMysteryEncounter([ - Species.CHARIZARD, - Species.VOLCARONA - ]); + await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); }); it("", async () => { - await game.runToMysteryEncounter([ - Species.CHARIZARD, - Species.VOLCARONA - ]); + await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 9fd989e82b8..2a7d3de3700 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -35,20 +35,14 @@ describe("Mystery Encounter Phases", () => { describe("MysteryEncounterPhase", () => { it("Runs to MysteryEncounterPhase", async() => { - await game.runToMysteryEncounter([ - Species.CHARIZARD, - Species.VOLCARONA - ]); + await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); }); it("Runs MysteryEncounterPhase", async() => { - await game.runToMysteryEncounter([ - Species.CHARIZARD, - Species.VOLCARONA - ]); + await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { // End phase early for test @@ -65,10 +59,7 @@ describe("Mystery Encounter Phases", () => { it("Selects an option for MysteryEncounterPhase", async() => { const dialogueSpy = vi.spyOn(game.scene.ui, "showDialogue"); const messageSpy = vi.spyOn(game.scene.ui, "showText"); - await game.runToMysteryEncounter([ - Species.CHARIZARD, - Species.VOLCARONA - ]); + await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]); game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { const handler = game.scene.ui.getHandler() as MessageUiHandler; diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 1b8c5c383a0..7ccec8113b3 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -36,6 +36,8 @@ import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler"; import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phases"; import { OverridesHelper } from "./overridesHelper"; +import { expect } from "vitest"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; /** * Class to manage the game state and transitions between phases. @@ -144,10 +146,11 @@ export default class GameManager { /** * Runs the game to a mystery encounter phase. + * @param encounterType - if specified, will expect encounter to have been spawned * @param species - Optional array of species for party. * @returns A promise that resolves when the EncounterPhase ends. */ - async runToMysteryEncounter(species?: Species[]) { + async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) { await this.runToTitle(); this.onNextPrompt("TitlePhase", Mode.TITLE, () => { @@ -164,6 +167,9 @@ export default class GameManager { }, () => this.isCurrentPhase(MysteryEncounterPhase), true); await this.phaseInterceptor.run(EncounterPhase); + if (encounterType) { + expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType); + } } /** diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 97caeafd7a6..ff32f41e849 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -5,6 +5,7 @@ import { MockInstance, vi } from "vitest"; import GameManager from "#test/utils/gameManager"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import * as overrides from "#app/overrides"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; /** * Helper to handle overrides in tests @@ -28,6 +29,17 @@ export class OverridesHelper { return spy; } + /** + * Override the encounter chance for a mystery encounter. + * @returns spy instance + * @param tier + */ + mysteryEncounterTier(tier: MysteryEncounterTier): MockInstance { + const spy = vi.spyOn(Overrides, "MYSTERY_ENCOUNTER_TIER_OVERRIDE", "get").mockReturnValue(tier); + this.log(`Mystery encounter tier set to ${tier}!`); + return spy; + } + /** * Override the encounter that spawns for the scene * @param encounterType @@ -63,11 +75,11 @@ export class OverridesHelper { /** * Override each wave to have or not have standard trainer battles * @returns spy instance - * @param isTrainer + * @param disable - true */ - trainerWave(isTrainer: boolean): MockInstance { - const spy = vi.spyOn(this.game.scene.gameMode, "isWaveTrainer").mockReturnValue(isTrainer); - this.log(`${isTrainer? "forcing" : "ignoring"} trainer waves!`); + disableTrainerWave(disable: boolean): MockInstance { + const spy = vi.spyOn(this.game.scene.gameMode, "isWaveTrainer").mockReturnValue(!disable); + this.log(`Standard trainer waves are ${disable? "disabled" : "enabled"}!`); return spy; } From e765b3a455e1ead3e7bd51167422c69a479d6bf7 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:25:02 -0400 Subject: [PATCH 24/27] Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- .../mystery-encounters/encounters/fiery-fallout-encounter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index b99a7092902..8fad5e69a6c 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -202,8 +202,8 @@ export const FieryFalloutEncounter: IMysteryEncounter = .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically - .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3PrimaryName dialogue token automatically + .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true, 1)) // Will set option3SecondaryName dialogue token automatically .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, From 88141759062b543fd74ce858e60d56329100405c Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:56:55 -0400 Subject: [PATCH 25/27] Apply suggestions from code review Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/data/mystery-encounters/utils/encounter-phase-utils.ts | 6 +++--- src/test/utils/overridesHelper.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 9c569775f38..b98a6597414 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -636,11 +636,11 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: // Transition scene.tweens.add({ targets: introVisuals, - x: hide ? "+=16" : "-=16", - y: hide ? "-=16" : "+=16", + x: `${hide? "+" : "-"}=16`, + y: `${hide ? "-" : "+"}=16`, alpha: hide ? 0 : 1, ease: "Sine.easeInOut", - duration: duration, + duration, onComplete: () => { if (hide && destroy) { scene.field.remove(introVisuals); diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 257ec523399..4116810be5d 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -45,7 +45,7 @@ export class OverridesHelper { * @param encounterType * @returns spy instance */ - mysteryEncounter(encounterType: MysteryEncounterType): MockInstance { + mysteryEncounter(encounterType: MysteryEncounterType) { const spy = vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(encounterType); this.log(`Mystery encounter override set to ${encounterType}!`); return spy; From e795a626291da3eef84d941016a0e020af4be03a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 20:06:49 -0400 Subject: [PATCH 26/27] add jsdocs and more cleanup --- .../encounters/safari-zone-encounter.ts | 2 +- .../mystery-encounters/mystery-encounter.ts | 29 ++++++++++++++----- .../utils/encounter-phase-utils.ts | 14 +++++++-- src/field/mystery-encounter-intro.ts | 4 +++ src/field/pokemon.ts | 1 + src/phases.ts | 5 ++-- src/phases/mystery-encounter-phases.ts | 20 ++++++------- .../mystery-encounter/encounterTestUtils.ts | 10 +++++++ .../fiery-fallout-encounter.test.ts | 2 +- src/test/utils/TextInterceptor.ts | 3 ++ 10 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index e5c5bd39b4d..28c60a0eb10 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -64,7 +64,7 @@ export const SafariZoneEncounter: IMysteryEncounter = .withOptionPhase(async (scene: BattleScene) => { // Start safari encounter const encounter = scene.currentBattle.mysteryEncounter; - encounter.encounterVariant = MysteryEncounterVariant.REPEATED_ENCOUNTER; + encounter.encounterVariant = MysteryEncounterVariant.CONTINUOUS_ENCOUNTER; encounter.misc = { safariPokemonRemaining: 3 }; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 6136e338854..026c58bb0c3 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -28,7 +28,7 @@ export enum MysteryEncounterVariant { BOSS_BATTLE, NO_BATTLE, /** For spawning new encounter queries instead of continuing to next wave */ - REPEATED_ENCOUNTER + CONTINUOUS_ENCOUNTER } /** @@ -115,11 +115,6 @@ export default interface IMysteryEncounter { * You probably shouldn't do anything with this unless you have a very specific need */ introVisuals?: MysteryEncounterIntroVisuals; - /** - * Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave - * You should never need to modify this - */ - seedOffset?: any; /** * Flags @@ -172,6 +167,12 @@ export default interface IMysteryEncounter { * Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class */ export default class IMysteryEncounter implements IMysteryEncounter { + /** + * Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave + * You should only need to interact via getter/update methods + */ + private seedOffset?: any; + constructor(encounter: IMysteryEncounter) { if (!isNullOrUndefined(encounter)) { Object.assign(this, encounter); @@ -371,6 +372,20 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.dialogueTokens[key] = value; } + getSeedOffset?() { + return this.seedOffset; + } + + /** + * Maintains seed offset for RNG consistency + * Increments if the same MysteryEncounter has multiple option select cycles + * @param scene + */ + updateSeedOffset?(scene: BattleScene) { + const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000; + this.seedOffset = currentOffset + 512; + } + private capitalizeFirstLetter?(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } @@ -442,7 +457,7 @@ export class MysteryEncounterBuilder implements Partial { * @param callback - {@linkcode OptionPhaseCallback} * @returns */ - withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this { + withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick { return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build()); } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 9c569775f38..8408dbfdd2c 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -30,6 +30,10 @@ import { Gender } from "#app/data/gender"; import { Moves } from "#enums/moves"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +/** + * Animates exclamation sprite over trainer's head at start of encounter + * @param scene + */ export function doTrainerExclamation(scene: BattleScene) { const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim"); exclamationSprite.setName("exclamation"); @@ -476,6 +480,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel()); const partyMemberExp = []; + // EXP value calculation is based off Pokemon.getExpValue let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1); if (participantIds?.length > 0) { @@ -597,7 +602,7 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: // If in repeated encounter variant, do nothing // Variant must eventually be swapped in order to handle "true" end of the encounter - if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.REPEATED_ENCOUNTER) { + if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.CONTINUOUS_ENCOUNTER) { return; } else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) { scene.pushPhase(new EggLapsePhase(scene)); @@ -657,7 +662,12 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: }); } -export function handleEncounterStartOfBattleEffects(scene: BattleScene) { +/** + * Will queue moves for any pokemon to use before the first CommandPhase of a battle + * Mostly useful for allowing MysteryEncounter enemies to "cheat" and use moves before the first turn + * @param scene + */ +export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) { const encounter = scene.currentBattle?.mysteryEncounter; if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter.encounterVariant !== MysteryEncounterVariant.NO_BATTLE && !encounter.startOfBattleEffectsComplete) { const effects = encounter.startOfBattleEffects; diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index fb894869adf..9ae83f49fab 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -349,6 +349,10 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con }); } + /** + * Sets container and all child sprites to visible + * @param value - true for visible, false for hidden + */ setVisible(value: boolean): this { this.getSprites().forEach(sprite => { sprite.setVisible(value); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 65fd982cbdc..b0974bd0aa8 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -101,6 +101,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public battleSummonData: PokemonBattleSummonData; public turnData: PokemonTurnData; + /** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */ public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null; public fieldPosition: FieldPosition; diff --git a/src/phases.ts b/src/phases.ts index 22a7b8bdd98..87e8cde9f5d 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -67,7 +67,7 @@ import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { doTrainerExclamation, handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { doTrainerExclamation, handleMysteryEncounterBattleStartEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -2053,8 +2053,7 @@ export class TurnInitPhase extends FieldPhase { //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); - // Start of battle effects for Mystery Encounters - handleEncounterStartOfBattleEffects(this.scene); + handleMysteryEncounterBattleStartEffects(this.scene); this.scene.getField().forEach((pokemon, i) => { if (pokemon?.isActive()) { diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 25f6b140140..11ff4766695 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -28,6 +28,8 @@ import { BattlerTagLapseType } from "#app/data/battler-tags"; export class MysteryEncounterPhase extends Phase { optionSelectSettings: OptionSelectSettings; + private FIRST_DIALOGUE_PROMPT_DELAY = 300; + /** * * @param scene @@ -46,9 +48,7 @@ export class MysteryEncounterPhase extends Phase { this.scene.clearPhaseQueue(); this.scene.clearPhaseQueueSplice(); - // Generates seed offset for RNG consistency, but incremented if the same MysteryEncounter has multiple option select cycles - const offset = this.scene.currentBattle.mysteryEncounter.seedOffset ?? this.scene.currentBattle.waveIndex * 1000; - this.scene.currentBattle.mysteryEncounter.seedOffset = offset + 512; + this.scene.currentBattle.mysteryEncounter.updateSeedOffset(this.scene); if (!this.optionSelectSettings) { // Sets flag that ME was encountered, only if this is not a followup option select phase @@ -79,7 +79,7 @@ export class MysteryEncounterPhase extends Phase { this.continueEncounter(); } }); - }, this.scene.currentBattle.mysteryEncounter.seedOffset); + }, this.scene.currentBattle.mysteryEncounter.getSeedOffset()); } else { this.continueEncounter(); } @@ -109,9 +109,9 @@ export class MysteryEncounterPhase extends Phase { } if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 300 : 0); + this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); } else { - this.scene.ui.showText(text, null, nextAction, i === 0 ? 300 : 0, true); + this.scene.ui.showText(text, null, nextAction, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); } i++; }; @@ -154,14 +154,14 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { this.onOptionSelect(this.scene).finally(() => { this.end(); }); - }, this.scene.currentBattle.mysteryEncounter.seedOffset); + }, this.scene.currentBattle.mysteryEncounter.getSeedOffset()); }); } else { this.scene.executeWithSeedOffset(() => { this.onOptionSelect(this.scene).finally(() => { this.end(); }); - }, this.scene.currentBattle.mysteryEncounter.seedOffset); + }, this.scene.currentBattle.mysteryEncounter.getSeedOffset()); } } } @@ -279,7 +279,7 @@ export class MysteryEncounterBattlePhase extends Phase { } else { const trainer = this.scene.currentBattle.trainer; let message: string; - scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.seedOffset); + scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.getSeedOffset()); const showDialogueAndSummon = () => { scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => { @@ -438,7 +438,7 @@ export class PostMysteryEncounterPhase extends Phase { this.continueEncounter(); } }); - }, this.scene.currentBattle.mysteryEncounter.seedOffset); + }, this.scene.currentBattle.mysteryEncounter.getSeedOffset()); } else { this.continueEncounter(); } diff --git a/src/test/mystery-encounter/encounterTestUtils.ts b/src/test/mystery-encounter/encounterTestUtils.ts index 218f2cdb971..88233032ccd 100644 --- a/src/test/mystery-encounter/encounterTestUtils.ts +++ b/src/test/mystery-encounter/encounterTestUtils.ts @@ -7,6 +7,12 @@ import GameManager from "../utils/gameManager"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { Status, StatusEffect } from "#app/data/status-effect"; +/** + * Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected + * @param game + * @param optionNo - human number, not index + * @param isBattle - if selecting option should lead to battle, set to true + */ export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) { // Handle any eventual queued messages (e.g. weather phase, etc.) game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { @@ -81,6 +87,10 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN } } +/** + * For any MysteryEncounter that has a battle, can call this to skip battle and proceed to MysteryEncounterRewardsPhase + * @param game + */ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager) { game.scene.clearPhaseQueue(); game.scene.clearPhaseQueueSplice(); diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 9bcb3a36d2c..20d0426e02d 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -74,7 +74,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.startingBiome(Biome.MOUNTAIN); await game.runToMysteryEncounter(); - expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA); + expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); }); it("should not run below wave 41", async () => { diff --git a/src/test/utils/TextInterceptor.ts b/src/test/utils/TextInterceptor.ts index a49f41f6be0..c209ece7e04 100644 --- a/src/test/utils/TextInterceptor.ts +++ b/src/test/utils/TextInterceptor.ts @@ -1,3 +1,6 @@ +/** + * Class will intercept any text or dialogue message calls and log them for test purposes + */ export default class TextInterceptor { private scene; public logs = []; From e3c1c3bb8c1fe8f3f201367746146f8d152ac2c4 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 20:12:33 -0400 Subject: [PATCH 27/27] add more jsdoc --- src/data/mystery-encounters/mystery-encounter.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 026c58bb0c3..b18707968a3 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -372,6 +372,13 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.dialogueTokens[key] = value; } + /** + * If an encounter uses {@link MysteryEncounterVariant.CONTINUOUS_ENCOUNTER}, + * should rely on this value for seed offset instead of wave index. + * + * This offset is incremented for each new {@link MysteryEncounterPhase} that occurs, + * so multi-encounter RNG will be consistent on resets and not be affected by number of turns, move RNG, etc. + */ getSeedOffset?() { return this.seedOffset; }