From 7b2f21dc8c4efb6018c8c6775b1bfc640750c89a Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Fri, 26 Jul 2024 23:02:15 -0400 Subject: [PATCH] finish absolute avarice encounter --- .../encounters/absolute-avarice-encounter.ts | 388 ++++++++++++------ .../encounters/fiery-fallout-encounter.ts | 9 +- .../encounters/pokemon-salesman-encounter.ts | 2 +- .../shady-vitamin-dealer-encounter.ts | 10 +- .../mystery-encounters/mystery-encounter.ts | 2 +- .../utils/encounter-pokemon-utils.ts | 44 +- .../absolute-avarice-dialogue.ts | 13 +- src/overrides.ts | 4 +- 8 files changed, 312 insertions(+), 160 deletions(-) diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index f64d387a7fe..a63fc527324 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -1,21 +1,26 @@ -import { EnemyPartyConfig, generateModifierTypeOption, leaveEncounterWithoutBattle, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import Pokemon from "#app/field/pokemon"; -import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; +import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import Pokemon, { PokemonMove } from "#app/field/pokemon"; +import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import BattleScene from "#app/battle-scene"; import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; -import { MoneyRequirement, PersistentModifierRequirement } from "../mystery-encounter-requirements"; +import { PersistentModifierRequirement } from "../mystery-encounter-requirements"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { BerryModifier } from "#app/modifier/modifier"; -import { ModifierRewardPhase, StatChangePhase } from "#app/phases"; +import { StatChangePhase } from "#app/phases"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BattleStat } from "#app/data/battle-stat"; +import { randInt } from "#app/utils"; +import { BattlerIndex } from "#app/battle"; +import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { TrainerSlot } from "#app/data/trainer-config"; +import { PokeballType } from "#app/data/pokeball"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:absoluteAvarice"; @@ -92,15 +97,6 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = hidden: true, disableAnimation: true }, - { - spriteKey: "petaya_berry", - fileRoot: "items", - isItem: true, - x: 20, - y: -17, - hidden: true, - disableAnimation: true - }, { spriteKey: "enigma_berry", fileRoot: "items", @@ -119,6 +115,15 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = hidden: true, disableAnimation: true }, + { + spriteKey: "petaya_berry", + fileRoot: "items", + isItem: true, + x: 30, + y: -17, + hidden: true, + disableAnimation: true + }, { spriteKey: "ganlon_berry", fileRoot: "items", @@ -136,7 +141,8 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = y: -2, hidden: true, disableAnimation: true - }, { + }, + { spriteKey: "starf_berry", fileRoot: "items", isItem: true, @@ -146,92 +152,11 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = disableAnimation: true }, ]) + .withHideWildIntroMessage(true) + .withAutoHideIntroVisuals(false) .withOnVisualsStart((scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - const greedentSprites = encounter.introVisuals.getSpriteAtIndex(0); - - scene.playSound("Follow Me"); - - // scene.tweens.add({ - // targets: greedentSprites, - // duration: 600, - // ease: "Cubic.easeOut", - // yoyo: true, - // y: "+=50", - // x: "-=60", - // scale: 1.2, - // onComplete: () => { - // // Bounce the Greedent - // scene.tweens.add({ - // targets: greedentSprites, - // duration: 300, - // ease: "Cubic.easeOut", - // yoyo: true, - // y: "-=20", - // loop: 1, - // }); - // } - // }); - - // Slide left - scene.tweens.add({ - targets: greedentSprites, - duration: 500, - ease: "Cubic.easeOut", - x: "-=300", - onComplete: () => { - // Slide back right, lower - greedentSprites[0].y += 80; - greedentSprites[1].y += 80; - scene.tweens.add({ - targets: greedentSprites, - duration: 300, - ease: "Cubic.easeOut", - yoyo: true, - x: "+=140", - onComplete: () => { - // Slide back right, higher - greedentSprites[0].y -= 80; - greedentSprites[1].y -= 80; - scene.tweens.add({ - targets: greedentSprites, - duration: 500, - ease: "Cubic.easeOut", - x: "+=300", - onComplete: () => { - // Bounce the Greedent - scene.tweens.add({ - targets: greedentSprites, - duration: 300, - ease: "Cubic.easeOut", - yoyo: true, - y: "-=20", - loop: 1, - }); - } - }); - } - }); - } - }); - - const berryAddDelay = 200; - - const animationOrder = ["starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa"]; - - animationOrder.forEach((berry, i) => { - const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey.includes(berry)); - const [ sprite, tintSprite ] = encounter.introVisuals.getSpriteAtIndex(introVisualsIndex); - // const [ sprite, tintSprite ] = [berrySprites[i * 2], berrySprites[i * 2 + 1]]; - scene.time.delayedCall(berryAddDelay * i + 300, () => { - if (sprite) { - sprite.setVisible(true); - } - if (tintSprite) { - tintSprite.setVisible(true); - } - }); - }); + doGreedentSpriteSteal(scene); + doBerrySpritePile(scene); return true; }) @@ -251,8 +176,8 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = .withOnInit((scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; + scene.loadSe("PRSFX- Bug Bite", "battle_anims"); scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3"); - // scene.loadSe("Follow Me", "battle_anims"); // Get all player berry items, remove from party, and store reference const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; @@ -288,13 +213,13 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = { species: getPokemonSpecies(Species.GREEDENT), isBoss: true, - bossSegments: 5, + bossSegments: 3, // nature: Nature.BOLD, moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF], modifierTypes: bossModifierTypes, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { - queueEncounterMessage(pokemon.scene, `${namespace}:option:2:stat_boost`); + queueEncounterMessage(pokemon.scene, `${namespace}:option:1:boss_enraged`); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1)); } } @@ -317,15 +242,32 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = }, ], }) - .withPreOptionPhase(async (scene: BattleScene): Promise => { - const encounter = scene.currentBattle.mysteryEncounter; - updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false); - return true; - }) .withOptionPhase(async (scene: BattleScene) => { - // Give the player an Ability Charm - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM)); - leaveEncounterWithoutBattle(scene, true); + // Pick battle + const encounter = scene.currentBattle.mysteryEncounter; + + // Provides 1x Reviver Seed to each party member at end of battle + const revSeed = generateModifierTypeOption(scene, modifierTypes.REVIVER_SEED).type; + const givePartyPokemonReviverSeeds = () => { + const party = scene.getParty(); + party.forEach(p => { + const seedModifier = revSeed.newModifier(p); + scene.addModifier(seedModifier, false, false, false, true); + }); + queueEncounterMessage(scene, `${namespace}:option:1:food_stash`); + }; + + setEncounterRewards(scene, { fillRemaining: true }, null, givePartyPokemonReviverSeeds); + encounter.startOfBattleEffects.push( + { + sourceBattlerIndex: BattlerIndex.ENEMY, + targets: [BattlerIndex.ENEMY], + move: new PokemonMove(Moves.STUFF_CHEEKS), + ignorePp: true + }); + + transitionMysteryEncounterIntroVisuals(scene, true, true, 500); + await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); }) .build() ) @@ -335,7 +277,6 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = .withDialogue({ buttonLabel: `${namespace}:option:2:label`, buttonTooltip: `${namespace}:option:2:tooltip`, - secondOptionPrompt: `${namespace}:option:2:select_prompt`, selected: [ { text: `${namespace}:option:2:selected`, @@ -344,19 +285,27 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = }) .withOptionPhase(async (scene: BattleScene) => { const encounter = scene.currentBattle.mysteryEncounter; - const modifier = encounter.misc.chosenModifier; - // Give the player a Candy Jar if they gave a Berry, and a Healing Charm for Reviver Seed - if (modifier.type.name.includes("Berry")) { - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR)); - } else { - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM)); - } + const berryMap = encounter.misc.berryItemsMap; - // Remove the modifier if its stacks go to 0 - modifier.stackCount -= 1; - if (modifier.stackCount === 0) { - scene.removeModifier(modifier); - } + // Returns 2/5 of the berries stolen from each Pokemon + const party = scene.getParty(); + party.forEach(pokemon => { + const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id); + const berryTypesAsArray = []; + stolenBerries?.forEach(bMod => berryTypesAsArray.push(...new Array(bMod.stackCount).fill(bMod.berryType))); + const returnedBerryCount = Math.floor((berryTypesAsArray.length ?? 0) * 2 / 5); + + if (returnedBerryCount > 0) { + for (let i = 0; i < returnedBerryCount; i++) { + // Shuffle remaining berry types and pop + Phaser.Math.RND.shuffle(berryTypesAsArray); + const randBerryType = berryTypesAsArray.pop(); + + const berryModType = generateModifierTypeOption(scene, modifierTypes.BERRY, [randBerryType]).type as BerryModifierType; + applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType); + } + } + }); leaveEncounterWithoutBattle(scene, true); }) @@ -368,27 +317,196 @@ export const AbsoluteAvariceEncounter: IMysteryEncounter = .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, - secondOptionPrompt: `${namespace}:option:3:select_prompt`, selected: [ { text: `${namespace}:option:3:selected`, }, ], }) + .withPreOptionPhase(async (scene: BattleScene) => { + // Animate berries being eaten + doGreedentEatBerries(scene); + doBerrySpritePile(scene, true); + return true; + }) .withOptionPhase(async (scene: BattleScene) => { - const encounter = scene.currentBattle.mysteryEncounter; - const modifier = encounter.misc.chosenModifier; - // Give the player a Berry Pouch - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH)); - - // Remove the modifier if its stacks go to 0 - modifier.stackCount -= 1; - if (modifier.stackCount === 0) { - scene.removeModifier(modifier); - } + // Let it have the food + // Greedent joins the team, level equal to 2 below highest party member + const level = getHighestLevelPlayerPokemon(scene).level; + const greedent = scene.addEnemyPokemon(getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false); + greedent.moveset = [new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF)]; + greedent.passive = true; + await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false); leaveEncounterWithoutBattle(scene, true); }) .build() ) .build(); + +function doGreedentSpriteSteal(scene: BattleScene) { + const shakeDelay = 50; + const slideDelay = 500; + + const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(0); + + scene.playSound("Follow Me"); + scene.tweens.chain({ + targets: greedentSprites, + tweens: [ + { // Slide Greedent diagonally + duration: slideDelay, + ease: "Cubic.easeOut", + y: "+=75", + x: "-=65", + scale: 1.1 + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Shake + duration: shakeDelay, + ease: "Cubic.easeOut", + yoyo: true, + x: (randInt(2) > 0 ? "-=" : "+=") + 5, + y: (randInt(2) > 0 ? "-=" : "+=") + 5, + }, + { // Slide Greedent diagonally + duration: slideDelay, + ease: "Cubic.easeOut", + y: "-=75", + x: "+=65", + scale: 1 + }, + { // Bounce at the end + duration: 300, + ease: "Cubic.easeOut", + yoyo: true, + y: "-=20", + loop: 1, + } + ] + }); +} + +function doGreedentEatBerries(scene: BattleScene) { + const greedentSprites = scene.currentBattle.mysteryEncounter.introVisuals.getSpriteAtIndex(0); + + // scene.playSound("Follow Me"); + let index = 1; + scene.tweens.add({ + targets: greedentSprites, + duration: 150, + ease: "Cubic.easeOut", + yoyo: true, + y: "-=8", + loop: 5, + onStart: () => { + scene.playSound("PRSFX- Bug Bite"); + }, + onLoop: () => { + if (index % 2 === 0) { + scene.playSound("PRSFX- Bug Bite"); + } + index++; + } + }); +} + +/** + * + * @param scene + * @param isEat - default false. Will "create" pile when false, and remove pile when true. + */ +function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) { + const berryAddDelay = 150; + let animationOrder = ["starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa"]; + if (isEat) { + animationOrder = animationOrder.reverse(); + } + const encounter = scene.currentBattle.mysteryEncounter; + animationOrder.forEach((berry, i) => { + const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey.includes(berry)); + const [ sprite, tintSprite ] = encounter.introVisuals.getSpriteAtIndex(introVisualsIndex); + scene.time.delayedCall(berryAddDelay * i + 400, () => { + if (sprite) { + sprite.setVisible(!isEat); + } + if (tintSprite) { + tintSprite.setVisible(!isEat); + } + + // Animate Petaya berry falling off the pile + if (berry === "petaya" && sprite && tintSprite && !isEat) { + scene.time.delayedCall(200, () => { + doBerryBounce(scene, [sprite, tintSprite], 30, 500); + }); + } + }); + }); +} + +function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: integer) { + let bouncePower = 1; + let bounceYOffset = yd; + + const doBounce = () => { + scene.tweens.add({ + targets: berrySprites, + y: "+=" + bounceYOffset, + x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, + duration: bouncePower * baseBounceDuration, + ease: "Cubic.easeIn", + onComplete: () => { + bouncePower = bouncePower > 0.01 ? bouncePower * 0.5 : 0; + + if (bouncePower) { + bounceYOffset = bounceYOffset * bouncePower; + + scene.tweens.add({ + targets: berrySprites, + y: "-=" + bounceYOffset, + x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, + duration: bouncePower * baseBounceDuration, + ease: "Cubic.easeOut", + onComplete: () => doBounce() + }); + } + } + }); + }; + + doBounce(); +} diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 2da9ade0ff0..c7933129ff9 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -1,6 +1,6 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { modifierTypes, } from "#app/modifier/modifier-type"; +import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"; @@ -17,7 +17,7 @@ import { WeatherType } from "#app/data/weather"; import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { StatusEffect } from "#app/data/status-effect"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -246,9 +246,8 @@ function giveLeadPokemonCharcoal(scene: BattleScene) { // Give first party pokemon Charcoal for free at end of battle const leadPokemon = scene.getParty()?.[0]; if (leadPokemon) { - const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); - scene.addModifier(charcoal.type.newModifier(leadPokemon), true); - scene.updateModifiers(); + const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]).type as AttackTypeBoosterModifierType; + applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal); scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.name); queueEncounterMessage(scene, `${namespace}:found_charcoal`); } diff --git a/src/data/mystery-encounters/encounters/pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/pokemon-salesman-encounter.ts index b5625cd349d..d1750119add 100644 --- a/src/data/mystery-encounters/encounters/pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/pokemon-salesman-encounter.ts @@ -133,7 +133,7 @@ export const PokemonSalesmanEncounter: IMysteryEncounter = // "Catch" purchased pokemon const data = new PokemonData(purchasedPokemon); data.player = false; - await catchPokemon(scene, data.toPokemon(scene) as EnemyPokemon, null, PokeballType.POKEBALL, true); + await catchPokemon(scene, data.toPokemon(scene) as EnemyPokemon, null, PokeballType.POKEBALL, true, true); leaveEncounterWithoutBattle(scene, true); }) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 19248d7a5bd..bd7afdaa832 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -10,7 +10,7 @@ import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MoneyRequirement } from "../mystery-encounter-requirements"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -110,10 +110,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = const modifiers = encounter.misc.modifiers; for (const modType of modifiers) { - const modifier = modType.newModifier(chosenPokemon); - await scene.addModifier(modifier, true, false, false, true); + await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); } - scene.updateModifiers(true); leaveEncounterWithoutBattle(scene); }) @@ -195,10 +193,8 @@ export const ShadyVitaminDealerEncounter: IMysteryEncounter = const modifiers = encounter.misc.modifiers; for (const modType of modifiers) { - const modifier = modType.newModifier(chosenPokemon); - await scene.addModifier(modifier, true, false, false, true); + await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType); } - scene.updateModifiers(true); leaveEncounterWithoutBattle(scene); }) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 51815e3ccb7..502d84d9124 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -167,13 +167,13 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; this.autoHideIntroVisuals = !isNullOrUndefined(this.autoHideIntroVisuals) ? this.autoHideIntroVisuals : true; - this.startOfBattleEffects = this.startOfBattleEffects ?? []; // Reset any dirty flags or encounter data this.startOfBattleEffectsComplete = false; this.lockEncounterRewardTiers = true; this.dialogueTokens = {}; this.enemyPartyConfigs = []; + this.startOfBattleEffects = []; this.introVisuals = null; this.misc = null; this.expMultiplier = 1; diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index b33607c6443..a11e65c5c54 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -17,7 +17,7 @@ import { Type } from "#app/data/type"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getPokemonNameWithAffix } from "#app/messages"; -import { modifierTypes } from "#app/modifier/modifier-type"; +import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; export interface MysteryEncounterPokemonData { spriteScale?: number @@ -233,6 +233,36 @@ export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: numb pokemon.calculateStats(); } +/** + * Will attempt to add a new modifier to a Pokemon. + * If the Pokemon already has max stacks of that item, it will instead apply 'fallbackModifierType', if specified. + * @param scene + * @param pokemon + * @param modType + * @param fallbackModifierType + */ +export async function applyModifierTypeToPlayerPokemon(scene: BattleScene, pokemon: PlayerPokemon, modType: PokemonHeldItemModifierType, fallbackModifierType?: PokemonHeldItemModifierType) { + // Check if the Pokemon has max stacks of that item already + const existing = scene.findModifier(m => ( + m instanceof PokemonHeldItemModifier && + m.type.id === modType.id && + m.pokemonId === pokemon.id + )) as PokemonHeldItemModifier; + + // At max stacks + if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { + if (!fallbackModifierType) { + return; + } + + // Apply fallback + return applyModifierTypeToPlayerPokemon(scene, pokemon, fallbackModifierType); + } + + const modifier = modType.newModifier(pokemon); + await scene.addModifier(modifier, false, false, false, true); +} + /** * Alternative to using AttemptCapturePhase * Assumes player sprite is visible on the screen (this is intended for non-combat uses) @@ -407,7 +437,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number, }); } -export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType, isObtain: boolean = false): Promise { +export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType, showCatchObtainMessage: boolean = true, isObtain: boolean = false): Promise { scene.unshiftPhase(new VictoryPhase(scene, BattlerIndex.ENEMY)); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); @@ -433,7 +463,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); return new Promise(resolve => { - scene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.name }), null, () => { + const doPokemonCatchMenu = () => { const end = () => { scene.pokemonInfoContainer.hide(); removePb(scene, pokeball); @@ -488,7 +518,13 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po addToParty(); } }); - }, 0, true); + }; + + if (showCatchObtainMessage) { + scene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.name }), null, doPokemonCatchMenu, 0, true); + } else { + doPokemonCatchMenu(); + } }); } diff --git a/src/locales/en/mystery-encounters/absolute-avarice-dialogue.ts b/src/locales/en/mystery-encounters/absolute-avarice-dialogue.ts index 69796a88d86..5d0935282f6 100644 --- a/src/locales/en/mystery-encounters/absolute-avarice-dialogue.ts +++ b/src/locales/en/mystery-encounters/absolute-avarice-dialogue.ts @@ -7,7 +7,10 @@ export const absoluteAvariceDialogue = { 1: { label: "Battle It", tooltip: "(-) Tough Battle\n(+) Rewards from its Berry Hoard", - selected: "You'll show this Greedent what\nhappens to those who steal from you!", + selected: "The Greedent stuffs its cheeks\nand prepares for battle!", + boss_enraged: "Greedent's fierce love for food has it incensed!", + food_stash: `It looks like the Greedent was guarding an enormous stash of food! + $@s{item_fanfare}Each Pokémon in your party gains 1x Reviver Seed!` }, 2: { label: "Reason with It", @@ -18,10 +21,10 @@ export const absoluteAvariceDialogue = { 3: { label: "Let It Have the Food", tooltip: "(-) Lose All Berries\n(?) The Greedent Will Like You", - selected: `The Greedent devours the entire stash of berries in a flash! - $Patting its stomach, it looks at you appreciatively. - $Perhaps you could feed it more berries on your adventure... - $The Greedent wants to join your party!`, + selected: `The Greedent devours the entire\nstash of berries in a flash! + $Patting its stomach,\nit looks at you appreciatively. + $Perhaps you could feed it\nmore berries on your adventure... + $@s{level_up_fanfare}The Greedent wants to join your party!`, }, } }; diff --git a/src/overrides.ts b/src/overrides.ts index b530032364d..1d006630983 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.ABSOLUTE_AVARICE; /** * MODIFIER / ITEM OVERRIDES