From 9e96ee84c5fcdbb1b68696892c3581f086f48021 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 16:26:34 -0400 Subject: [PATCH 1/8] stash working changes --- .../mystery-encounters/berry_juice.json | 41 ++++ .../images/mystery-encounters/berry_juice.png | Bin 0 -> 318 bytes .../encounters/one-for-all-encounter.ts | 217 ++++++++++++++++++ .../encounters/sleeping-snorlax-encounter.ts | 1 + .../utils/encounter-phase-utils.ts | 8 + .../utils/encounter-pokemon-utils.ts | 4 + src/field/pokemon.ts | 4 + src/overrides.ts | 4 +- src/phases.ts | 1 + 9 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 public/images/mystery-encounters/berry_juice.json create mode 100644 public/images/mystery-encounters/berry_juice.png create mode 100644 src/data/mystery-encounters/encounters/one-for-all-encounter.ts diff --git a/public/images/mystery-encounters/berry_juice.json b/public/images/mystery-encounters/berry_juice.json new file mode 100644 index 00000000000..88242213344 --- /dev/null +++ b/public/images/mystery-encounters/berry_juice.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "berry_juice.png", + "format": "RGBA8888", + "size": { + "w": 24, + "h": 23 + }, + "scale": 1, + "frames": [ + { + "filename": "Bag_Berry_Juice_Sprite.png", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 24, + "h": 24 + }, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 22, + "h": 21 + }, + "frame": { + "x": 1, + "y": 1, + "w": 22, + "h": 21 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:04685a0eb6ef9095824b65408ec1b38f:9891674d538df100fcddde29330c21ae:927f117bdb1c2a27226a5540ce00ee8b$" + } +} diff --git a/public/images/mystery-encounters/berry_juice.png b/public/images/mystery-encounters/berry_juice.png new file mode 100644 index 0000000000000000000000000000000000000000..c0986b804f9ff176b4c5f0fb09e49d829f7ccd68 GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^5dmR!KK*shpv+vr1Zd!JR$t)%Tk3Y|hC38E9K7xbKjbyLUv(`U$f`BaU8KWAJ!~ z>XY^@w$fb%7 literal 0 HcmV?d00001 diff --git a/src/data/mystery-encounters/encounters/one-for-all-encounter.ts b/src/data/mystery-encounters/encounters/one-for-all-encounter.ts new file mode 100644 index 00000000000..a07bec924a9 --- /dev/null +++ b/src/data/mystery-encounters/encounters/one-for-all-encounter.ts @@ -0,0 +1,217 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { + EnemyPartyConfig, EnemyPokemonConfig, + initBattleWithEnemyConfig, + leaveEncounterWithoutBattle, + setEncounterRewards +} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; +import Pokemon from "#app/field/pokemon"; +import { ModifierTier } from "#app/modifier/modifier-tier"; +import { + getPartyLuckValue, + getPlayerModifierTypeOptions, + ModifierPoolType, + ModifierTypeOption, + regenerateModifierPoolThresholds, +} from "#app/modifier/modifier-type"; +import { StatChangePhase } from "#app/phases"; +import { randSeedInt } from "#app/utils"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import BattleScene from "../../../battle-scene"; +import IMysteryEncounter, { + MysteryEncounterBuilder, + MysteryEncounterTier, +} from "../mystery-encounter"; +import { MoveRequirement } from "../mystery-encounter-requirements"; +import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#app/data/status-effect"; + +/** the i18n namespace for the encounter */ +const namespace = "mysteryEncounter:oneForAll"; + +export const OneForAllEncounter: IMysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.FIGHT_OR_FLIGHT + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withHideWildIntroMessage(true) + .withIntroSpriteConfigs([ + { + spriteKey: Species.SHUCKLE.toString(), + fileRoot: "pokemon", + hasShadow: true, + repeat: true, + scale: 2 + }, + { + spriteKey: "berry_juice", + fileRoot: "mystery-encounters", + hasShadow: true, + scale: 2 + }, + ]) // Set in onInit() + .withIntroDialogue([ + { + text: `${namespace}_intro_message`, + }, + ]) + .withOnInit((scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + + // Calculate boss mon + const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 1, + pokemonConfigs: [{ species: bossSpecies, isBoss: true }], + }; + encounter.enemyPartyConfigs = [config]; + + // Calculate item + // 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER + const tier = + scene.currentBattle.waveIndex > 160 + ? ModifierTier.MASTER + : scene.currentBattle.waveIndex > 110 + ? ModifierTier.ROGUE + : scene.currentBattle.waveIndex > 60 + ? ModifierTier.ULTRA + : ModifierTier.GREAT; + regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); + const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0]; + encounter.setDialogueToken("itemName", item.type.name); + encounter.misc = item; + + const bossSpriteKey = bossSpecies.getSpriteId(false, bossSpecies.forms ? 0 : null, false, bossSpecies.hasVariants() ? 0 : null); + encounter.spriteConfigs = [ + { + spriteKey: item.type.iconImage, + fileRoot: "items", + hasShadow: false, + x: 35, + y: -5, + scale: 0.75, + isItem: true, + }, + { + spriteKey: bossSpriteKey, + fileRoot: "pokemon", + hasShadow: true, + tint: 0.25, + x: -5, + repeat: true, + }, + ]; + + // If player has a stealing move, they succeed automatically + encounter.options[1].meetsRequirements(scene); + const primaryPokemon = encounter.options[1].primaryPokemon; + if (primaryPokemon) { + // Use primaryPokemon to execute the thievery + encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`; + } else { + encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`; + } + + 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_message`, + }, + ], + }, + async (scene: BattleScene) => { + // Pick battle + const encounter = scene.currentBattle.mysteryEncounter; + const bossSpecies = getPokemonSpecies(Species.SNORLAX); + const pokemonConfig: EnemyPokemonConfig = { + species: bossSpecies, + isBoss: true, + status: StatusEffect.SLEEP, + }; + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 2, + pokemonConfigs: [pokemonConfig], + }; + encounter.enemyPartyConfigs = [config]; + + const item = scene.currentBattle.mysteryEncounter + .misc as ModifierTypeOption; + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); + await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); + } + ) + .withOption( + new MysteryEncounterOptionBuilder() + .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`, + }) + .withOptionPhase(async (scene: BattleScene) => { + // Pick steal + const encounter = scene.currentBattle.mysteryEncounter; + const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; + setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); + + // If player has a stealing move, they succeed automatically + const primaryPokemon = encounter.options[1].primaryPokemon; + if (primaryPokemon) { + // Use primaryPokemon to execute the thievery + await showEncounterText(scene, `${namespace}_option_2_steal_result`); + leaveEncounterWithoutBattle(scene); + return; + } + + const roll = randSeedInt(16); + if (roll > 6) { + // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) + 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); + 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 initBattleWithEnemyConfig(scene, config); + } else { + // Steal item (37.5%) + // Display result message then proceed to rewards + await showEncounterText(scene, `${namespace}_option_2_good_result`); + leaveEncounterWithoutBattle(scene); + } + }) + .build() + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_3_label`, + buttonTooltip: `${namespace}_option_3_tooltip`, + selected: [ + { + text: `${namespace}_option_3_selected`, + }, + ], + }, + async (scene: BattleScene) => { + // Leave encounter with no rewards or exp + leaveEncounterWithoutBattle(scene, true); + return true; + } + ) + .build(); diff --git a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts index 9d3659f9443..0bb7f4bfd52 100644 --- a/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/sleeping-snorlax-encounter.ts @@ -50,6 +50,7 @@ export const SleepingSnorlaxEncounter: IMysteryEncounter = species: bossSpecies, isBoss: true, status: StatusEffect.SLEEP, + spriteScale: 1.5 }; const config: EnemyPartyConfig = { levelAdditiveMultiplier: 2, diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ffaf11b8219..8ad022f881c 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -40,6 +40,7 @@ export class EnemyPokemonConfig { mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; status?: StatusEffect; passive?: boolean; + spriteScale?: number; } export class EnemyPartyConfig { @@ -162,6 +163,13 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.formIndex = config.formIndex; } + // Set scale + if (!isNullOrUndefined(config.spriteScale)) { + enemyPokemon.mysteryEncounterData = { + spriteScale: config.spriteScale + }; + } + // Set Boss if (config.isBoss) { let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index fdbb957161f..48bfbb183e4 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -17,6 +17,10 @@ import { Type } from "#app/data/type"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +export interface MysteryEncounterPokemonData { + spriteScale?: number +} + /** * * Will never remove the player's last non-fainted Pokemon (if they only have 1) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ebae5fe8122..5eea5d9c4b7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -50,6 +50,7 @@ import { BerryType } from "#enums/berry-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; export enum FieldPosition { CENTER, @@ -100,6 +101,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public battleData: PokemonBattleData; public battleSummonData: PokemonBattleSummonData; public turnData: PokemonTurnData; + public mysteryEncounterData: MysteryEncounterPokemonData; public fieldPosition: FieldPosition; @@ -521,6 +523,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const formKey = this.getFormKey(); if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) { return 1.5; + } else if (this?.mysteryEncounterData?.spriteScale) { + return this.mysteryEncounterData.spriteScale; } return 1; } 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 f98c914e320..c0ecea19eb7 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1667,6 +1667,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.getSprite().clearTint(); pokemon.resetSummonData(); + this.scene.updateFieldScale(); this.scene.time.delayedCall(1000, () => this.end()); } }); From b6793ea50fffb4c7b0df82cb765cf39c93475e97 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 17:20:39 -0400 Subject: [PATCH 2/8] stash changes --- .../juiced_shuckle-juice-encounter.ts | 122 ++++++++++ .../encounters/one-for-all-encounter.ts | 217 ------------------ .../utils/encounter-phase-utils.ts | 21 +- src/enums/mystery-encounter-type.ts | 3 +- 4 files changed, 141 insertions(+), 222 deletions(-) create mode 100644 src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts delete mode 100644 src/data/mystery-encounters/encounters/one-for-all-encounter.ts diff --git a/src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts b/src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts new file mode 100644 index 00000000000..e30e3612761 --- /dev/null +++ b/src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts @@ -0,0 +1,122 @@ +import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, 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"; +import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; +import { Species } from "#enums/species"; +import { Nature } from "#app/data/nature"; + +/** the i18n namespace for the encounter */ +const namespace = "mysteryEncounter:oneForAll"; + +export const JuicedShuckleJuiceEncounter: IMysteryEncounter = + MysteryEncounterBuilder.withEncounterType( + MysteryEncounterType.JUICED_SHUCKLE_JUICE + ) + .withEncounterTier(MysteryEncounterTier.COMMON) + .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 + .withHideWildIntroMessage(true) + .withIntroSpriteConfigs([ + { + spriteKey: Species.SHUCKLE.toString(), + fileRoot: "pokemon", + hasShadow: true, + repeat: true, + scale: 2 + }, + { + spriteKey: "berry_juice", + fileRoot: "mystery-encounters", + hasShadow: true, + scale: 2 + }, + ]) // Set in onInit() + .withIntroDialogue([ + { + text: `${namespace}_intro_message`, + }, + ]) + .withOnInit((scene: BattleScene) => { + const encounter = scene.currentBattle.mysteryEncounter; + + // Calculate boss mon + const config: EnemyPartyConfig = { + levelAdditiveMultiplier: 1, + pokemonConfigs: [ + { + species: getPokemonSpecies(Species.SHUCKLE), + isBoss: true, + bossSegments: 5, + spriteScale: 2, + nature: Nature.BOLD, + // moves: [Moves.INFESTATION, Moves.SALT_CURE, Moves.STEALTH_ROCK, Moves.RECOVER] + } + ], + }; + encounter.enemyPartyConfigs = [config]; + + 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) => { + // Leave encounter with no rewards or exp + // const encounter = scene.currentBattle.mysteryEncounter; + + // Get highest BST mon + const party = scene.getParty(); + let highestBst = null; + let statTotal = 0; + for (const pokemon of party) { + if (!highestBst) { + highestBst = pokemon; + statTotal = pokemon.summonData.stats.reduce((i, n) => n + i); + continue; + } + + const total = pokemon.summonData.stats.reduce((i, n) => n + i); + if (total > statTotal) { + highestBst = pokemon; + statTotal = total; + } + } + + if (!highestBst) { + highestBst = party[0]; + } + + + + leaveEncounterWithoutBattle(scene, true); + return true; + } + ) + .withSimpleOption( + { + buttonLabel: `${namespace}_option_2_label`, + buttonTooltip: `${namespace}_option_2_tooltip`, + selected: [ + { + text: `${namespace}_option_2_selected_message`, + }, + ], + }, + async (scene: BattleScene) => { + // Pick battle + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], fillRemaining: true }); + await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); + } + ) + .build(); diff --git a/src/data/mystery-encounters/encounters/one-for-all-encounter.ts b/src/data/mystery-encounters/encounters/one-for-all-encounter.ts deleted file mode 100644 index a07bec924a9..00000000000 --- a/src/data/mystery-encounters/encounters/one-for-all-encounter.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { BattleStat } from "#app/data/battle-stat"; -import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { - EnemyPartyConfig, EnemyPokemonConfig, - initBattleWithEnemyConfig, - leaveEncounterWithoutBattle, - setEncounterRewards -} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; -import Pokemon from "#app/field/pokemon"; -import { ModifierTier } from "#app/modifier/modifier-tier"; -import { - getPartyLuckValue, - getPlayerModifierTypeOptions, - ModifierPoolType, - ModifierTypeOption, - regenerateModifierPoolThresholds, -} from "#app/modifier/modifier-type"; -import { StatChangePhase } from "#app/phases"; -import { randSeedInt } from "#app/utils"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import BattleScene from "../../../battle-scene"; -import IMysteryEncounter, { - MysteryEncounterBuilder, - MysteryEncounterTier, -} from "../mystery-encounter"; -import { MoveRequirement } from "../mystery-encounter-requirements"; -import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { getPokemonSpecies } from "#app/data/pokemon-species"; -import { Species } from "#enums/species"; -import { StatusEffect } from "#app/data/status-effect"; - -/** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:oneForAll"; - -export const OneForAllEncounter: IMysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.FIGHT_OR_FLIGHT - ) - .withEncounterTier(MysteryEncounterTier.COMMON) - .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 - .withHideWildIntroMessage(true) - .withIntroSpriteConfigs([ - { - spriteKey: Species.SHUCKLE.toString(), - fileRoot: "pokemon", - hasShadow: true, - repeat: true, - scale: 2 - }, - { - spriteKey: "berry_juice", - fileRoot: "mystery-encounters", - hasShadow: true, - scale: 2 - }, - ]) // Set in onInit() - .withIntroDialogue([ - { - text: `${namespace}_intro_message`, - }, - ]) - .withOnInit((scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - - // Calculate boss mon - const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true); - const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 1, - pokemonConfigs: [{ species: bossSpecies, isBoss: true }], - }; - encounter.enemyPartyConfigs = [config]; - - // Calculate item - // 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER - const tier = - scene.currentBattle.waveIndex > 160 - ? ModifierTier.MASTER - : scene.currentBattle.waveIndex > 110 - ? ModifierTier.ROGUE - : scene.currentBattle.waveIndex > 60 - ? ModifierTier.ULTRA - : ModifierTier.GREAT; - regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); - const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0]; - encounter.setDialogueToken("itemName", item.type.name); - encounter.misc = item; - - const bossSpriteKey = bossSpecies.getSpriteId(false, bossSpecies.forms ? 0 : null, false, bossSpecies.hasVariants() ? 0 : null); - encounter.spriteConfigs = [ - { - spriteKey: item.type.iconImage, - fileRoot: "items", - hasShadow: false, - x: 35, - y: -5, - scale: 0.75, - isItem: true, - }, - { - spriteKey: bossSpriteKey, - fileRoot: "pokemon", - hasShadow: true, - tint: 0.25, - x: -5, - repeat: true, - }, - ]; - - // If player has a stealing move, they succeed automatically - encounter.options[1].meetsRequirements(scene); - const primaryPokemon = encounter.options[1].primaryPokemon; - if (primaryPokemon) { - // Use primaryPokemon to execute the thievery - encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`; - } else { - encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`; - } - - 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_message`, - }, - ], - }, - async (scene: BattleScene) => { - // Pick battle - const encounter = scene.currentBattle.mysteryEncounter; - const bossSpecies = getPokemonSpecies(Species.SNORLAX); - const pokemonConfig: EnemyPokemonConfig = { - species: bossSpecies, - isBoss: true, - status: StatusEffect.SLEEP, - }; - const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 2, - pokemonConfigs: [pokemonConfig], - }; - encounter.enemyPartyConfigs = [config]; - - const item = scene.currentBattle.mysteryEncounter - .misc as ModifierTypeOption; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); - await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]); - } - ) - .withOption( - new MysteryEncounterOptionBuilder() - .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`, - }) - .withOptionPhase(async (scene: BattleScene) => { - // Pick steal - const encounter = scene.currentBattle.mysteryEncounter; - const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption; - setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false }); - - // If player has a stealing move, they succeed automatically - const primaryPokemon = encounter.options[1].primaryPokemon; - if (primaryPokemon) { - // Use primaryPokemon to execute the thievery - await showEncounterText(scene, `${namespace}_option_2_steal_result`); - leaveEncounterWithoutBattle(scene); - return; - } - - const roll = randSeedInt(16); - if (roll > 6) { - // Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%) - 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); - 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 initBattleWithEnemyConfig(scene, config); - } else { - // Steal item (37.5%) - // Display result message then proceed to rewards - await showEncounterText(scene, `${namespace}_option_2_good_result`); - leaveEncounterWithoutBattle(scene); - } - }) - .build() - ) - .withSimpleOption( - { - buttonLabel: `${namespace}_option_3_label`, - buttonTooltip: `${namespace}_option_3_tooltip`, - selected: [ - { - text: `${namespace}_option_3_selected`, - }, - ], - }, - async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - leaveEncounterWithoutBattle(scene, true); - return true; - } - ) - .build(); diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 8ad022f881c..92acc65326c 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -26,6 +26,7 @@ import PokemonSpecies from "../../pokemon-species"; import { Status, StatusEffect } from "../../status-effect"; import { TrainerConfig, trainerConfigs, TrainerSlot } from "../../trainer-config"; import { MysteryEncounterVariant } from "../mystery-encounter"; +import { Nature } from "#app/data/nature"; export class EnemyPokemonConfig { species: PokemonSpecies; @@ -34,6 +35,8 @@ export class EnemyPokemonConfig { bossSegmentModifier?: number; // Additive to the determined segment number formIndex?: number; level?: number; + nature?: Nature; + ivs?: [integer, integer, integer, integer, integer, integer]; modifierTypes?: PokemonHeldItemModifierType[]; dataSource?: PokemonData; tags?: BattlerTagType[]; @@ -180,15 +183,25 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: } // Set Passive - if (partyConfig.pokemonConfigs[e].passive) { + if (config.passive) { enemyPokemon.passive = true; } + // Set Nature + if (config.nature) { + enemyPokemon.nature = config.nature; + } + + // Set IVs + if (config.ivs) { + enemyPokemon.ivs = config.ivs; + } + // Set Status - if (partyConfig.pokemonConfigs[e].status) { + if (config.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 cureTurn = config.status === StatusEffect.SLEEP ? 3 : null; + enemyPokemon.status = new Status(config.status, 0, cureTurn); } // Set tags diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 1f2711cd674..7a9938091c1 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -9,5 +9,6 @@ export enum MysteryEncounterType { SHADY_VITAMIN_DEALER, FIELD_TRIP, SAFARI_ZONE, - LOST_AT_SEA //might be generalized later on + LOST_AT_SEA, //might be generalized later on + JUICED_SHUCKLE_JUICE } From 46f8d26100fcc7dafce114c95d753f8607b6e0e5 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 19 Jul 2024 23:40:36 -0400 Subject: [PATCH 3/8] add the strong stuff encounter --- .../mystery-encounters/berry_juice.json | 2 +- .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/safari-zone-encounter.ts | 2 +- .../shady-vitamin-dealer-encounter.ts | 4 +- ...unter.ts => the-strong-stuff-encounter.ts} | 87 +++++++++++++------ .../encounters/training-session-encounter.ts | 2 +- .../mystery-encounters/mystery-encounters.ts | 2 + src/data/pokemon-species.ts | 8 ++ src/enums/mystery-encounter-type.ts | 2 +- src/field/mystery-encounter-intro.ts | 10 +-- src/locales/en/mystery-encounter.ts | 2 + .../the-strong-stuff-dialogue.ts | 25 ++++++ src/overrides.ts | 2 +- src/ui/message-ui-handler.ts | 20 ++++- 14 files changed, 128 insertions(+), 42 deletions(-) rename src/data/mystery-encounters/encounters/{juiced_shuckle-juice-encounter.ts => the-strong-stuff-encounter.ts} (50%) create mode 100644 src/locales/en/mystery-encounters/the-strong-stuff-dialogue.ts diff --git a/public/images/mystery-encounters/berry_juice.json b/public/images/mystery-encounters/berry_juice.json index 88242213344..1a5331368ad 100644 --- a/public/images/mystery-encounters/berry_juice.json +++ b/public/images/mystery-encounters/berry_juice.json @@ -10,7 +10,7 @@ "scale": 1, "frames": [ { - "filename": "Bag_Berry_Juice_Sprite.png", + "filename": "0001.png", "rotated": false, "trimmed": true, "sourceSize": { diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 9a0a42af2c8..63ceea1dac9 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -28,7 +28,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = hasShadow: true, x: 4, y: 10, - yShadowOffset: 3, + yShadow: 3, disableAnimation: true, // Re-enabled after option select }, ]) diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 2b7a013dd22..aa7fd19391f 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -31,7 +31,7 @@ export const SafariZoneEncounter: IMysteryEncounter = hasShadow: true, x: 4, y: 10, - yShadowOffset: 3 + yShadow: 3 }, ]) .withIntroDialogue([ 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..b1beee30247 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -31,7 +31,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = repeat: true, x: 12, y: -5, - yShadowOffset: -5 + yShadow: -5 }, { spriteKey: "b2w2_veteran_m", @@ -39,7 +39,7 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = hasShadow: true, x: -12, y: 3, - yShadowOffset: 3 + yShadow: 3 }, ]) .withIntroDialogue([ diff --git a/src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts similarity index 50% rename from src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts rename to src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index e30e3612761..019eac98d7c 100644 --- a/src/data/mystery-encounters/encounters/juiced_shuckle-juice-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -1,4 +1,4 @@ -import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { EnemyPartyConfig, hideMysteryEncounterIntroVisuals, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, 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"; @@ -6,35 +6,44 @@ import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } fro import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Species } from "#enums/species"; import { Nature } from "#app/data/nature"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; /** the i18n namespace for the encounter */ -const namespace = "mysteryEncounter:oneForAll"; +const namespace = "mysteryEncounter:theStrongStuff"; -export const JuicedShuckleJuiceEncounter: IMysteryEncounter = +export const TheStrongStuffEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.JUICED_SHUCKLE_JUICE + MysteryEncounterType.THE_STRONG_STUFF ) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 180) // waves 10 to 180 .withHideWildIntroMessage(true) + .withHideIntroVisuals(false) .withIntroSpriteConfigs([ + { + spriteKey: "berry_juice", + fileRoot: "mystery-encounters", + hasShadow: true, + scale: 1.5, + x: -15, + y: 3, + yShadow: 0 + }, { spriteKey: Species.SHUCKLE.toString(), fileRoot: "pokemon", hasShadow: true, repeat: true, - scale: 2 - }, - { - spriteKey: "berry_juice", - fileRoot: "mystery-encounters", - hasShadow: true, - scale: 2 + scale: 1.5, + x: 20, + y: 10, + yShadow: 7 }, ]) // Set in onInit() .withIntroDialogue([ { - text: `${namespace}_intro_message`, + text: `${namespace}:intro`, }, ]) .withOnInit((scene: BattleScene) => { @@ -58,35 +67,39 @@ export const JuicedShuckleJuiceEncounter: 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` + } + ] }, async (scene: BattleScene) => { - // Leave encounter with no rewards or exp - // const encounter = scene.currentBattle.mysteryEncounter; + const encounter = scene.currentBattle.mysteryEncounter; + // Do blackout and hide intro visuals during blackout + scene.time.delayedCall(700, () => { + hideMysteryEncounterIntroVisuals(scene); + }); + // -20 to all base stats of highest BST, +10 to all base stats of rest of party // Get highest BST mon const party = scene.getParty(); - let highestBst = null; + let highestBst: PlayerPokemon = null; let statTotal = 0; for (const pokemon of party) { if (!highestBst) { highestBst = pokemon; - statTotal = pokemon.summonData.stats.reduce((i, n) => n + i); + statTotal = pokemon.getSpeciesForm().getBaseStatTotal(); continue; } - const total = pokemon.summonData.stats.reduce((i, n) => n + i); + const total = pokemon.getSpeciesForm().getBaseStatTotal(); if (total > statTotal) { highestBst = pokemon; statTotal = total; @@ -97,7 +110,25 @@ export const JuicedShuckleJuiceEncounter: IMysteryEncounter = highestBst = party[0]; } + console.log("BST pre change: " + highestBst.getSpeciesForm().baseStats); + highestBst.getSpeciesForm().baseStats = [...highestBst.getSpeciesForm().baseStats].map(v => v - 20); + console.log("Species BST: " + getPokemonSpecies(highestBst.getSpeciesForm().speciesId).baseStats); + console.log("Pokemon BST: " + highestBst.getSpeciesForm().baseStats); + highestBst.calculateStats(); + highestBst.updateInfo(); + for (const pokemon of party) { + if (highestBst.id === pokemon.id) { + continue; + } + pokemon.getSpeciesForm().baseStats = [...pokemon.getSpeciesForm().baseStats].map(v => v + 10); + // pokemon.summonData.getSpeciesForm()Form.baseStats = pokemon.getSpeciesForm().baseStats; + pokemon.calculateStats(); + pokemon.updateInfo(); + } + + encounter.setDialogueToken("highBstPokemon", highestBst.name); + await showEncounterText(scene, `${namespace}:option:1:selected_2`, null, true); leaveEncounterWithoutBattle(scene, true); return true; @@ -105,11 +136,11 @@ export const JuicedShuckleJuiceEncounter: 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`, }, ], }, diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 1c0c3633883..fd4c7d0e7cc 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -35,7 +35,7 @@ export const TrainingSessionEncounter: IMysteryEncounter = hasShadow: true, y: 6, x: 5, - yShadowOffset: -2 + yShadow: -2 }, ]) .withIntroDialogue([ diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index e70c00d90c8..6d16be83d22 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -12,6 +12,7 @@ import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounte import { TrainingSessionEncounter } from "./encounters/training-session-encounter"; import IMysteryEncounter from "./mystery-encounter"; import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; +import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; @@ -220,6 +221,7 @@ export function initMysteryEncounters() { allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter; allMysteryEncounters[MysteryEncounterType.SAFARI_ZONE] = SafariZoneEncounter; allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter; + allMysteryEncounters[MysteryEncounterType.THE_STRONG_STUFF] = TheStrongStuffEncounter; // Add extreme encounters to biome map extremeBiomeEncounters.forEach(encounter => { diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 2b488f330c4..26af9155687 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -221,6 +221,14 @@ export abstract class PokemonSpeciesForm { return false; } + /** + * Gets the BST for the species + * @returns The species' BST. + */ + getBaseStatTotal(): integer { + return this.baseStats.reduce((i, n) => n + i); + } + /** * Gets the species' base stat amount for the given stat. * @param stat The desired stat. diff --git a/src/enums/mystery-encounter-type.ts b/src/enums/mystery-encounter-type.ts index 7a9938091c1..7f8ed906fa7 100644 --- a/src/enums/mystery-encounter-type.ts +++ b/src/enums/mystery-encounter-type.ts @@ -10,5 +10,5 @@ export enum MysteryEncounterType { FIELD_TRIP, SAFARI_ZONE, LOST_AT_SEA, //might be generalized later on - JUICED_SHUCKLE_JUICE + THE_STRONG_STUFF } diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index bacf107f482..4767e58d79f 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -39,7 +39,7 @@ export class MysteryEncounterSpriteConfig { /** Y offset */ y?: number; /** Y shadow offset */ - yShadowOffset?: number; + yShadow?: number; /** Sprite scale. `0` - `n` */ scale?: number; /** If you are using an item sprite, set to `true` */ @@ -70,10 +70,10 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con return; } - const getSprite = (spriteKey: string, hasShadow?: boolean, yShadowOffset?: number) => { + const getSprite = (spriteKey: string, hasShadow?: boolean, yShadow?: number) => { const ret = this.scene.addFieldSprite(0, 0, spriteKey); ret.setOrigin(0.5, 1); - ret.setPipeline(this.scene.spritePipeline, { tone: [0.0, 0.0, 0.0, 0.0], hasShadow: !!hasShadow, yShadowOffset: yShadowOffset ?? 0 }); + ret.setPipeline(this.scene.spritePipeline, { tone: [0.0, 0.0, 0.0, 0.0], hasShadow: !!hasShadow, yShadowOffset: yShadow ?? 0 }); return ret; }; @@ -92,13 +92,13 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1)); this.spriteConfigs?.forEach((config) => { - const { spriteKey, isItem, hasShadow, scale, x, y, yShadowOffset, alpha } = config; + const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha } = config; let sprite: GameObjects.Sprite; let tintSprite: GameObjects.Sprite; if (!isItem) { - sprite = getSprite(spriteKey, hasShadow, yShadowOffset); + sprite = getSprite(spriteKey, hasShadow, yShadow); tintSprite = getSprite(spriteKey); } else { sprite = getItemSprite(spriteKey); diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index 47c601cc9db..5effff0b02a 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,4 +1,5 @@ import { lostAtSea } from "./mystery-encounters/lost-at-sea"; +import { theStrongStuffDialogue } from "#app/locales/en/mystery-encounters/the-strong-stuff-dialogue"; /** * Patterns that can be used: @@ -241,4 +242,5 @@ export const mysteryEncounter = { "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, + theStrongStuff: theStrongStuffDialogue, } as const; diff --git a/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.ts b/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.ts new file mode 100644 index 00000000000..a165c9718a8 --- /dev/null +++ b/src/locales/en/mystery-encounters/the-strong-stuff-dialogue.ts @@ -0,0 +1,25 @@ +export const theStrongStuffDialogue = { + intro: "It's a massive Shuckle and what appears\nto be an equally large stash of... juice?", + title: "The Strong Stuff", + description: "The Shuckle that blocks your path looks incredibly strong. Meanwhile, the juice next to it is emanating power of some kind.\n\nThe Shuckle extends its feelers in your direction. It seems like it wants to touch you, but is that really a good idea?", + query: "What will you do?", + option: { + 1: { + label: "Let it touch you", + tooltip: "(?) Something awful or amazing might happen", + selected: "You black out.", + selected_2: `@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained. + $Your {{highBstPokemon}} feels a\nterrible lethargy come over it! + $It's base stats were reduced by 20 in each stat! + $Your remaining Pokémon feel an incredible vigor, though! + $Their base stats are increased by 10 in each stat!` + }, + 2: { + label: "Battle the Shuckle", + tooltip: "(-) Hard Battle\n(+) Special Rewards", + 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.", + }, + }, + outro: "You are back on track." +}; diff --git a/src/overrides.ts b/src/overrides.ts index 7272f68bcb4..d16895b27fa 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -119,7 +119,7 @@ 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_TIER_OVERRIDE: MysteryEncounterTier = null; -export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.SLEEPING_SNORLAX; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.THE_STRONG_STUFF; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index 05c91ca1643..cdd8b20b09d 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -32,7 +32,8 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { const charVarMap = new Map(); const delayMap = new Map(); const soundMap = new Map(); - const actionPattern = /@(c|d|s)\{(.*?)\}/; + const fadeMap = new Map(); + const actionPattern = /@(c|d|s|f)\{(.*?)\}/; let actionMatch: RegExpExecArray; while ((actionMatch = actionPattern.exec(text))) { switch (actionMatch[1]) { @@ -45,6 +46,9 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { case "s": soundMap.set(actionMatch.index, actionMatch[2]); break; + case "f": + fadeMap.set(actionMatch.index, parseInt(actionMatch[2])); + break; } text = text.slice(0, actionMatch.index) + text.slice(actionMatch.index + actionMatch[2].length + 4); } @@ -103,6 +107,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { const charVar = charVarMap.get(charIndex); const charSound = soundMap.get(charIndex); const charDelay = delayMap.get(charIndex); + const charFade = fadeMap.get(charIndex); this.message.setText(text.slice(0, charIndex)); const advance = () => { if (charVar) { @@ -134,6 +139,19 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { advance(); } }); + } else if (charFade) { + this.textTimer.paused = true; + this.scene.time.delayedCall(150, () => { + this.scene.ui.fadeOut(750).then(() => { + const delay = Utils.getFrameMs(charFade); + this.scene.time.delayedCall(delay, () => { + this.scene.ui.fadeIn(500).then(() => { + this.textTimer.paused = false; + advance(); + }); + }); + }); + }); } else { advance(); } From d810af0b57b7d4633cc67405288d9b55ff696167 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Sat, 20 Jul 2024 13:45:24 -0400 Subject: [PATCH 4/8] add the strong stuff encounter and more unit tests --- .../utils/encounter-phase-utils.ts | 2 +- .../utils/encounter-pokemon-utils.ts | 2 +- src/overrides.ts | 4 +- src/phases.ts | 11 +- src/phases/mystery-encounter-phases.ts | 20 +- .../department-store-sale-encounter.test.ts | 240 ++++++++++++++++++ .../fiery-fallout-encounter.test.ts | 25 +- .../encounters/lost-at-sea-encounter.test.ts | 15 +- .../the-strong-stuff-encounter.test.ts | 231 +++++++++++++++++ .../mystery-encounter-utils.test.ts | 2 +- src/test/utils/gameManager.ts | 8 +- src/ui/mystery-encounter-ui-handler.ts | 8 +- 12 files changed, 512 insertions(+), 56 deletions(-) create mode 100644 src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts create mode 100644 src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ac8c54d266b..40763358b89 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -54,7 +54,7 @@ export function doTrainerExclamation(scene: BattleScene) { } }); - scene.playSound("GEN8- Exclaim.wav", { volume: 0.8 }); + scene.playSound("GEN8- Exclaim.wav", { volume: 0.7 }); } export interface EnemyPokemonConfig { diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 07955a08a7d..2dbbe9f55a1 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -207,7 +207,7 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h export function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) { pokemon.getSpeciesForm().baseStats = [...pokemon.getSpeciesForm().baseStats].map(v => { const newVal = Math.floor(v + value); - return Math.min(newVal, 1); + return Math.max(newVal, 1); }); pokemon.calculateStats(); pokemon.updateInfo(); diff --git a/src/overrides.ts b/src/overrides.ts index d16895b27fa..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.THE_STRONG_STUFF; +export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; /** * MODIFIER / ITEM OVERRIDES diff --git a/src/phases.ts b/src/phases.ts index 904e860935b..0ceff19f059 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1107,18 +1107,19 @@ export class EncounterPhase extends BattlePhase { if (showEncounterMessage) { const introDialogue = this.scene.currentBattle.mysteryEncounter.dialogue.intro; + const FIRST_DIALOGUE_PROMPT_DELAY = 750; let i = 0; const showNextDialogue = () => { const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue; const dialogue = introDialogue[i]; const title = getEncounterText(this.scene, dialogue.speaker); const text = getEncounterText(this.scene, dialogue.text); - if (title) { - this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0); - } else { - this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true); - } i++; + if (title) { + this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0); + } else { + this.scene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true); + } }; if (introDialogue.length > 0) { diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 11ff4766695..d55bcaad636 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -26,10 +26,9 @@ import { BattlerTagLapseType } from "#app/data/battler-tags"; * - Queuing of the MysteryEncounterOptionSelectedPhase */ export class MysteryEncounterPhase extends Phase { + private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300; optionSelectSettings: OptionSelectSettings; - private FIRST_DIALOGUE_PROMPT_DELAY = 300; - /** * * @param scene @@ -108,12 +107,12 @@ export class MysteryEncounterPhase extends Phase { title = getEncounterText(this.scene, dialogue.speaker); } - if (title) { - 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 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); - } i++; + if (title) { + this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); + } else { + this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); + } }; showNextDialogue(); @@ -420,6 +419,7 @@ export class MysteryEncounterRewardsPhase extends Phase { * - Queuing of the next wave */ export class PostMysteryEncounterPhase extends Phase { + private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750; onPostOptionSelect: OptionPhaseCallback; constructor(scene: BattleScene) { @@ -462,13 +462,13 @@ export class PostMysteryEncounterPhase extends Phase { title = getEncounterText(this.scene, dialogue.speaker); } + i++; this.scene.ui.setMode(Mode.MESSAGE); 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 === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0); } else { - this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true); + this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true); } - i++; }; showNextDialogue(); diff --git a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts new file mode 100644 index 00000000000..c8cbc88d54e --- /dev/null +++ b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -0,0 +1,240 @@ +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 * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option"; +import { runSelectMysteryEncounterOption } from "#test/mystery-encounter/encounterTestUtils"; +import { SelectModifierPhase } from "#app/phases"; +import BattleScene from "#app/battle-scene"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; +import { Mode } from "#app/ui/ui"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale-encounter"; +import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; + +const namespace = "mysteryEncounter:departmentStoreSale"; +const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; +const defaultBiome = Biome.PLAINS; +const defaultWave = 37; + +describe("Department Store Sale - Mystery Encounter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let scene: BattleScene; + + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); + + beforeEach(async () => { + 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.disableTrainerWave(true); + + const biomeMap = new Map([ + [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], + ]); + CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => { + biomeMap.set(biome, [MysteryEncounterType.DEPARTMENT_STORE_SALE]); + }); + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); + vi.resetAllMocks(); + }); + + it("should have the correct properties", async () => { + game.override.mysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE); + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + + expect(DepartmentStoreSaleEncounter.encounterType).toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); + expect(DepartmentStoreSaleEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); + expect(DepartmentStoreSaleEncounter.dialogue).toBeDefined(); + expect(DepartmentStoreSaleEncounter.dialogue.intro).toStrictEqual([ + { text: `${namespace}:intro` }, + { + speaker: `${namespace}:speaker`, + text: `${namespace}:intro_dialogue`, + } + ]); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`); + expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`); + expect(DepartmentStoreSaleEncounter.options.length).toBe(4); + }); + + it("should not spawn outside of CIVILIZATION_ENCOUNTER_BIOMES", async () => { + game.override.startingBiome(Biome.VOLCANO); + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); + }); + + it("should not run below wave 10", async () => { + game.override.startingWave(9); + + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); + }); + + it("should not run above wave 179", async () => { + game.override.startingWave(181); + + await game.runToMysteryEncounter(); + + expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); + }); + + describe("Option 1 - TM Shop", () => { + it("should have the correct properties", () => { + const option = DepartmentStoreSaleEncounter.options[0]; + expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:1:label`, + buttonTooltip: `${namespace}:option:1:tooltip`, + }); + }); + + it("should have shop with only TMs", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 1); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + await game.phaseInterceptor.run(SelectModifierPhase); + + expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; + expect(modifierSelectHandler.options.length).toEqual(4); + for (const option of modifierSelectHandler.options) { + expect(option.modifierTypeOption.type.id).toContain("TM_"); + } + }); + + it("should leave encounter without battle", async () => { + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 1); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); + + describe("Option 2 - Vitamin Shop", () => { + it("should have the correct properties", () => { + const option = DepartmentStoreSaleEncounter.options[1]; + expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:2:label`, + buttonTooltip: `${namespace}:option:2:tooltip`, + }); + }); + + it("should have shop with only Vitamins", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 2); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + await game.phaseInterceptor.run(SelectModifierPhase); + + expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; + expect(modifierSelectHandler.options.length).toEqual(3); + for (const option of modifierSelectHandler.options) { + expect(option.modifierTypeOption.type.id.includes("PP_UP") || + option.modifierTypeOption.type.id.includes("BASE_STAT_BOOSTER")).toBeTruthy(); + } + }); + + it("should leave encounter without battle", async () => { + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 2); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); + + describe("Option 3 - X Item Shop", () => { + it("should have the correct properties", () => { + const option = DepartmentStoreSaleEncounter.options[2]; + expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:3:label`, + buttonTooltip: `${namespace}:option:3:tooltip`, + }); + }); + + it("should have shop with only X Items", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 3); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + await game.phaseInterceptor.run(SelectModifierPhase); + + expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; + expect(modifierSelectHandler.options.length).toEqual(5); + for (const option of modifierSelectHandler.options) { + expect(option.modifierTypeOption.type.id.includes("DIRE_HIT") || + option.modifierTypeOption.type.id.includes("TEMP_STAT_BOOSTER")).toBeTruthy(); + } + }); + + it("should leave encounter without battle", async () => { + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 3); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); + + describe("Option 4 - Pokeball Shop", () => { + it("should have the correct properties", () => { + const option = DepartmentStoreSaleEncounter.options[3]; + expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT); + expect(option.dialogue).toBeDefined(); + expect(option.dialogue).toStrictEqual({ + buttonLabel: `${namespace}:option:4:label`, + buttonTooltip: `${namespace}:option:4:tooltip`, + }); + }); + + it("should have shop with only Pokeballs", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 4); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + await game.phaseInterceptor.run(SelectModifierPhase); + + expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; + expect(modifierSelectHandler.options.length).toEqual(4); + for (const option of modifierSelectHandler.options) { + expect(option.modifierTypeOption.type.id).toContain("BALL"); + } + }); + + it("should leave encounter without battle", async () => { + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); + await runSelectMysteryEncounterOption(game, 4); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); +}); 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 20d0426e02d..6dd8593787e 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:fieryFallout"; /** 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; +const defaultWave = 56; describe("Fiery Fallout - Mystery Encounter", () => { let phaserGame: Phaser.Game; @@ -42,7 +42,6 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWave(true); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -54,6 +53,8 @@ describe("Fiery Fallout - Mystery Encounter", () => { afterEach(() => { game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); + vi.resetAllMocks(); }); it("should have the correct properties", async () => { @@ -74,7 +75,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { game.override.startingBiome(Biome.MOUNTAIN); await game.runToMysteryEncounter(); - expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT); }); it("should not run below wave 41", async () => { @@ -82,7 +83,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(); - expect(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 () => { @@ -96,7 +97,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { 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 moveInitSpy = vi.spyOn(BattleAnims, "initMoveAnim"); const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets"); const { onInit } = FieryFalloutEncounter; @@ -130,10 +131,6 @@ 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); @@ -180,14 +177,10 @@ 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", () => { - 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); @@ -235,10 +228,6 @@ 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); 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 04854574a77..5f2df5b20eb 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 @@ -32,7 +32,6 @@ describe("Lost at Sea - Mystery Encounter", () => { game.override.mysteryEncounterChance(100); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWave(true); vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ @@ -44,6 +43,8 @@ describe("Lost at Sea - Mystery Encounter", () => { afterEach(() => { game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); + vi.resetAllMocks(); }); it("should have the correct properties", async () => { @@ -99,10 +100,6 @@ 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); @@ -149,10 +146,6 @@ 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]; @@ -202,10 +195,6 @@ 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]; diff --git a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts new file mode 100644 index 00000000000..0df99c56cae --- /dev/null +++ b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -0,0 +1,231 @@ +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 Battle from "#app/battle"; +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 * as Modifiers from "#app/modifier/modifier"; +import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; +import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; +import { Nature } from "#app/data/nature"; +import { BerryType } from "#enums/berry-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { PokemonMove } from "#app/field/pokemon"; +import { Mode } from "#app/ui/ui"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; + +const namespace = "mysteryEncounter:theStrongStuff"; +const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; +const defaultBiome = Biome.CAVE; +const defaultWave = 45; + +describe("The Strong Stuff - Mystery Encounter", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let scene: BattleScene; + + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS }); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + scene = game.scene; + game.override.mysteryEncounterChance(100); + game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); + game.override.startingWave(defaultWave); + game.override.startingBiome(defaultBiome); + + vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( + new Map([ + [Biome.CAVE, [MysteryEncounterType.THE_STRONG_STUFF]], + [Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], + ]) + ); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); + vi.resetAllMocks(); + }); + + it("should have the correct properties", async () => { + game.override.mysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF); + await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); + + expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF); + expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); + expect(TheStrongStuffEncounter.dialogue).toBeDefined(); + expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`); + expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`); + expect(TheStrongStuffEncounter.options.length).toBe(2); + }); + + it("should not spawn outside of CAVE biome", async () => { + game.override.startingBiome(Biome.MOUNTAIN); + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF); + }); + + it("should not run below wave 10", async () => { + game.override.startingWave(9); + + await game.runToMysteryEncounter(); + + expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF); + }); + + it("should not run above wave 179", async () => { + game.override.startingWave(181); + + await game.runToMysteryEncounter(); + + expect(scene.currentBattle.mysteryEncounter).toBeUndefined(); + }); + + it("should initialize fully ", async () => { + vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: TheStrongStuffEncounter } as Battle); + const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets"); + const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets"); + + const { onInit } = TheStrongStuffEncounter; + + expect(TheStrongStuffEncounter.onInit).toBeDefined(); + + const onInitResult = onInit(scene); + + expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([ + { + levelAdditiveMultiplier: 1, + disableSwitch: true, + pokemonConfigs: [ + { + species: getPokemonSpecies(Species.SHUCKLE), + isBoss: true, + bossSegments: 5, + spriteScale: 1.5, + nature: Nature.BOLD, + moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER], + modifierTypes: expect.any(Array), + tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], + mysteryEncounterBattleEffects: expect.any(Function) + } + ], + } + ]); + await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled()); + await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled()); + expect(onInitResult).toBe(true); + }); + + describe("Option 1 - Power Swap BSTs", () => { + it("should have the correct properties", () => { + const option1 = TheStrongStuffEncounter.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 lower stats of highest BST and raise stats for rest of party", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); + + const bstsPrior = scene.getParty().map(p => p.getSpeciesForm().getBaseStatTotal()); + await runSelectMysteryEncounterOption(game, 1); + + const bstsAfter = scene.getParty().map(p => { + return p.getSpeciesForm().getBaseStatTotal(); + }); + + expect(bstsAfter[0]).toEqual(bstsPrior[0] - 20 * 6); + expect(bstsAfter[1]).toEqual(bstsPrior[1] + 10 * 6); + expect(bstsAfter[2]).toEqual(bstsPrior[2] + 10 * 6); + }); + + it("should leave encounter without battle", async () => { + const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); + + await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); + await runSelectMysteryEncounterOption(game, 1); + + expect(leaveEncounterWithoutBattleSpy).toBeCalled(); + }); + }); + + describe("Option 2 - battle the Shuckle", () => { + it("should have the correct properties", () => { + const option1 = TheStrongStuffEncounter.options[1]; + 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 start battle against Shuckle", async () => { + const phaseSpy = vi.spyOn(scene, "pushPhase"); + + await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); + await runSelectMysteryEncounterOption(game, 2, true); + + const enemyField = scene.getEnemyField(); + expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(enemyField.length).toBe(1); + expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE); + expect(enemyField[0].summonData.battleStats).toEqual([0, 2, 0, 2, 0, 0, 0]); + const shuckleItems = scene.getModifiers(Modifiers.BerryModifier, false); + expect(shuckleItems.length).toBe(4); + expect(shuckleItems.find(m => m.berryType === BerryType.SITRUS)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m.berryType === BerryType.GANLON)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m.berryType === BerryType.APICOT)?.stackCount).toBe(1); + expect(shuckleItems.find(m => m.berryType === BerryType.LUM)?.stackCount).toBe(2); + expect(enemyField[0].moveset).toEqual([new PokemonMove(Moves.INFESTATION), new PokemonMove(Moves.SALT_CURE), new PokemonMove(Moves.GASTRO_ACID), new PokemonMove(Moves.HEAL_ORDER)]); + + // Should have used moves pre-battle + const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]); + expect(movePhases.length).toBe(2); + expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.GASTRO_ACID).length).toBe(1); + expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.STEALTH_ROCK).length).toBe(1); + }); + + it("should have Soul Dew in rewards", async () => { + await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); + await runSelectMysteryEncounterOption(game, 2, true); + await skipBattleRunMysteryEncounterRewardsPhase(game); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name); + await game.phaseInterceptor.run(SelectModifierPhase); + + expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; + expect(modifierSelectHandler.options.length).toEqual(3); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("SOUL_DEW"); + }); + }); +}); diff --git a/src/test/mystery-encounter/mystery-encounter-utils.test.ts b/src/test/mystery-encounter/mystery-encounter-utils.test.ts index 2867bcc63bc..cdc7eda180f 100644 --- a/src/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/src/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -243,7 +243,7 @@ describe("Mystery Encounter Utils", () => { arceus.hp = 100; expect(arceus.isAllowedInBattle()).toBe(true); - koPlayerPokemon(arceus); + koPlayerPokemon(scene, arceus); expect(arceus.isAllowedInBattle()).toBe(false); }); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 7ccec8113b3..039f55c4621 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -38,6 +38,7 @@ import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phases"; import { OverridesHelper } from "./overridesHelper"; import { expect } from "vitest"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { isNullOrUndefined } from "#app/utils"; /** * Class to manage the game state and transitions between phases. @@ -151,6 +152,11 @@ export default class GameManager { * @returns A promise that resolves when the EncounterPhase ends. */ async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) { + if (!isNullOrUndefined(encounterType)) { + this.override.disableTrainerWave(true); + this.override.mysteryEncounter(encounterType); + } + await this.runToTitle(); this.onNextPrompt("TitlePhase", Mode.TITLE, () => { @@ -167,7 +173,7 @@ export default class GameManager { }, () => this.isCurrentPhase(MysteryEncounterPhase), true); await this.phaseInterceptor.run(EncounterPhase); - if (encounterType) { + if (!isNullOrUndefined(encounterType)) { expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType); } } diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 3031d3d24c5..788d8811db1 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -285,7 +285,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.cursor = cursor; } - this.viewPartyIndex = this.optionsContainer.length - 1; + this.viewPartyIndex = this.optionsContainer.list?.length - 1; if (!this.cursorObj) { this.cursorObj = this.scene.add.image(0, 0, "cursor"); @@ -294,11 +294,11 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (cursor === this.viewPartyIndex) { this.cursorObj.setPosition(246, -17); - } else if (this.optionsContainer.length === 3) { // 2 Options + } else if (this.optionsContainer.list?.length === 3) { // 2 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15); - } else if (this.optionsContainer.length === 4) { // 3 Options + } else if (this.optionsContainer.list?.length === 4) { // 3 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0)); - } else if (this.optionsContainer.length === 5) { // 4 Options + } else if (this.optionsContainer.list?.length === 5) { // 4 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0)); } From 0475946367639add93b7f7236a3933094764fb90 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Sat, 20 Jul 2024 13:50:47 -0400 Subject: [PATCH 5/8] cleanup container length checks in ME ui --- src/ui/mystery-encounter-ui-handler.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 788d8811db1..72cce41d55f 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -136,7 +136,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { // TODO: If we need to handle cancel option? Maybe default logic to leave/run from encounter idk } } else { - switch (this.optionsContainer.list.length) { + switch (this.optionsContainer.getAll()?.length) { + default: case 3: success = this.handleTwoOptionMoveInput(button); break; @@ -285,7 +286,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { this.cursor = cursor; } - this.viewPartyIndex = this.optionsContainer.list?.length - 1; + this.viewPartyIndex = this.optionsContainer.getAll()?.length - 1; if (!this.cursorObj) { this.cursorObj = this.scene.add.image(0, 0, "cursor"); @@ -294,11 +295,11 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (cursor === this.viewPartyIndex) { this.cursorObj.setPosition(246, -17); - } else if (this.optionsContainer.list?.length === 3) { // 2 Options + } else if (this.optionsContainer.getAll()?.length === 3) { // 2 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15); - } else if (this.optionsContainer.list?.length === 4) { // 3 Options + } else if (this.optionsContainer.getAll()?.length === 4) { // 3 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0)); - } else if (this.optionsContainer.list?.length === 5) { // 4 Options + } else if (this.optionsContainer.getAll()?.length === 5) { // 4 Options this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0)); } From 8de1ca8fec3085e2708df5b3bbee2bfd4587b099 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Sat, 20 Jul 2024 13:59:04 -0400 Subject: [PATCH 6/8] add retries to tests --- .github/workflows/mystery-events.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mystery-events.yml b/.github/workflows/mystery-events.yml index 66ab3dd1659..499ce03b136 100644 --- a/.github/workflows/mystery-events.yml +++ b/.github/workflows/mystery-events.yml @@ -50,4 +50,4 @@ jobs: run: npm ci # Use 'npm ci' to install dependencies - name: tests # Step to run tests - run: npm run test${{ runner.debug == '0' &&':silent' || '' }} # silent on default. if debug run loud + run: npm run test${{ runner.debug == '0' &&':silent' || '' }} --retry=3 # silent on default. if debug run loud. Retry failed tests up to 3 times From c7ed5bee97dbce0e4500ee3a22826aec94bc8d83 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Sat, 20 Jul 2024 14:01:04 -0400 Subject: [PATCH 7/8] add retries to tests --- .github/workflows/mystery-events.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mystery-events.yml b/.github/workflows/mystery-events.yml index 499ce03b136..e2b756a9ca8 100644 --- a/.github/workflows/mystery-events.yml +++ b/.github/workflows/mystery-events.yml @@ -50,4 +50,4 @@ jobs: run: npm ci # Use 'npm ci' to install dependencies - name: tests # Step to run tests - run: npm run test${{ runner.debug == '0' &&':silent' || '' }} --retry=3 # silent on default. if debug run loud. Retry failed tests up to 3 times + run: npm run test${{ runner.debug == '0' &&':silent' || '' }} -- --retry=3 # silent on default. if debug run loud. Retry failed tests up to 3 times From 4133302c5dbabcea20b187cfa62721cb3e37f5c4 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Sat, 20 Jul 2024 14:38:48 -0400 Subject: [PATCH 8/8] fix trainer wave disable override --- .github/workflows/mystery-events.yml | 2 +- .../department-store-sale-encounter.test.ts | 3 +-- .../encounters/fiery-fallout-encounter.test.ts | 1 - .../encounters/lost-at-sea-encounter.test.ts | 1 - .../encounters/the-strong-stuff-encounter.test.ts | 1 - src/test/mystery-encounter/mystery-encounter.test.ts | 2 -- src/test/phases/mystery-encounter-phase.test.ts | 1 - src/test/utils/gameManager.ts | 2 +- src/test/utils/overridesHelper.ts | 11 +++++++++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/mystery-events.yml b/.github/workflows/mystery-events.yml index e2b756a9ca8..621bffb3e2b 100644 --- a/.github/workflows/mystery-events.yml +++ b/.github/workflows/mystery-events.yml @@ -50,4 +50,4 @@ jobs: run: npm ci # Use 'npm ci' to install dependencies - name: tests # Step to run tests - run: npm run test${{ runner.debug == '0' &&':silent' || '' }} -- --retry=3 # silent on default. if debug run loud. Retry failed tests up to 3 times + run: npm run test${{ runner.debug == '0' &&':silent' || '' }} # silent on default. if debug run loud. diff --git a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index c8cbc88d54e..cbc5ab7950e 100644 --- a/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -36,7 +36,7 @@ describe("Department Store Sale - Mystery Encounter", () => { game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON); game.override.startingWave(defaultWave); game.override.startingBiome(defaultBiome); - game.override.disableTrainerWave(true); + game.override.disableTrainerWaves(true); const biomeMap = new Map([ [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], @@ -54,7 +54,6 @@ describe("Department Store Sale - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - game.override.mysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE); await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); expect(DepartmentStoreSaleEncounter.encounterType).toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE); 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 6dd8593787e..33845d1dfe5 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -58,7 +58,6 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT); await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); expect(FieryFalloutEncounter.encounterType).toBe(MysteryEncounterType.FIERY_FALLOUT); 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 5f2df5b20eb..6d36f47a8c1 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 @@ -48,7 +48,6 @@ describe("Lost at Sea - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA); await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, defaultParty); expect(LostAtSeaEncounter.encounterType).toBe(MysteryEncounterType.LOST_AT_SEA); diff --git a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 0df99c56cae..ed15380b8c9 100644 --- a/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -60,7 +60,6 @@ describe("The Strong Stuff - Mystery Encounter", () => { }); it("should have the correct properties", async () => { - game.override.mysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF); await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF); diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index ef0b5b3238a..0ba28bb77ec 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -23,8 +23,6 @@ describe("Mystery Encounters", () => { game = new GameManager(phaserGame); game.override.startingWave(11); game.override.mysteryEncounterChance(100); - game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); - game.override.disableTrainerWave(true); }); 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 2a7d3de3700..5c7c9958c80 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -28,7 +28,6 @@ describe("Mystery Encounter Phases", () => { game = new GameManager(phaserGame); game.override.startingWave(11); game.override.mysteryEncounterChance(100); - game.override.mysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); // Seed guarantees wild encounter to be replaced by ME game.override.seed("test"); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 039f55c4621..a8bd8ee3404 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -153,7 +153,7 @@ export default class GameManager { */ async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) { if (!isNullOrUndefined(encounterType)) { - this.override.disableTrainerWave(true); + this.override.disableTrainerWaves(true); this.override.mysteryEncounter(encounterType); } diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 4116810be5d..d2011bf4581 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -6,6 +6,8 @@ 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"; +import * as GameMode from "#app/game-mode"; +import { GameModes, getGameMode } from "#app/game-mode"; /** * Helper to handle overrides in tests @@ -77,8 +79,13 @@ export class OverridesHelper { * @returns spy instance * @param disable - true */ - disableTrainerWave(disable: boolean): MockInstance { - const spy = vi.spyOn(this.game.scene.gameMode, "isWaveTrainer").mockReturnValue(!disable); + disableTrainerWaves(disable: boolean): MockInstance { + const realFn = getGameMode; + const spy = vi.spyOn(GameMode, "getGameMode").mockImplementation((gameMode: GameModes) => { + const mode = realFn(gameMode); + mode.hasTrainers = !disable; + return mode; + }); this.log(`Standard trainer waves are ${disable? "disabled" : "enabled"}!`); return spy; }