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 00000000000..c0986b804f9 Binary files /dev/null and b/public/images/mystery-encounters/berry_juice.png differ 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()); } });