more ME balance changes and bug fixes

This commit is contained in:
ImperialSympathizer 2024-09-24 11:32:22 -04:00
parent b2b88c37cf
commit 1513f2a57d
21 changed files with 122 additions and 62 deletions

View File

@ -15,6 +15,7 @@ import { EggTier } from "#enums/egg-type";
import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:aTrainersTest"; const namespace = "mysteryEncounter:aTrainersTest";
@ -27,7 +28,7 @@ const namespace = "mysteryEncounter:aTrainersTest";
export const ATrainersTestEncounter: MysteryEncounter = export const ATrainersTestEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.A_TRAINERS_TEST) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.A_TRAINERS_TEST)
.withEncounterTier(MysteryEncounterTier.ROGUE) .withEncounterTier(MysteryEncounterTier.ROGUE)
.withSceneWaveRangeRequirement(100, 180) .withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withIntroSpriteConfigs([]) // These are set in onInit() .withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([ .withIntroDialogue([
{ {

View File

@ -159,12 +159,6 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
]) ])
.withHideWildIntroMessage(true) .withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withOnVisualsStart((scene: BattleScene) => {
doGreedentSpriteSteal(scene);
doBerrySpritePile(scene);
return true;
})
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}.intro`, text: `${namespace}.intro`,
@ -236,6 +230,9 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart((scene: BattleScene) => {
doGreedentSpriteSteal(scene);
doBerrySpritePile(scene);
// Remove the berries from the party // Remove the berries from the party
// Session has been safely saved at this point, so data won't be lost // Session has been safely saved at this point, so data won't be lost
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];

View File

@ -8,7 +8,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
@ -61,7 +61,14 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true);
const price = scene.getWaveMoneyAmount(10);
// Base value of Relic Gold, increased linearly up to 3x Relic Gold based on the starter tier of the Pokemon being purchased
// Starter value 1-3 -> 10x
// Starter value 10 -> 30x
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true);
const starterValue: number = speciesStarters[baseSpecies] ?? 1;
const multiplier = Math.max(3 * starterValue, 10);
const price = scene.getWaveMoneyAmount(multiplier);
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());

View File

@ -10,7 +10,7 @@ import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement }
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, LevelIncrementBoosterModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier"; import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
@ -33,7 +33,7 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
"PokemonBaseStatTotalModifier" "PokemonBaseStatTotalModifier"
]; ];
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5; const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
/** /**
* Delibird-y encounter. * Delibird-y encounter.
@ -122,9 +122,9 @@ export const DelibirdyEncounter: MysteryEncounter =
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
// Give the player an Ability Charm // Give the player an Amulet Coin
// Check if the player has max stacks of that item already // Check if the player has max stacks of that item already
const existing = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier; const existing = scene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
@ -133,7 +133,7 @@ export const DelibirdyEncounter: MysteryEncounter =
scene.playSound("item_fanfare"); scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM)); scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN));
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);

View File

@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:fieldTrip";
export const FieldTripEncounter: MysteryEncounter = export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
.withEncounterTier(MysteryEncounterTier.COMMON) .withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[0], 100)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "preschooler_m", spriteKey: "preschooler_m",

View File

@ -11,9 +11,10 @@ import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import * as Utils from "#app/utils";
import { IntegerHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils"; import { IntegerHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import i18next from "i18next"; import i18next from "i18next";
@ -24,6 +25,8 @@ import { getEncounterText, showEncounterText } from "#app/data/mystery-encounter
import { trainerNamePools } from "#app/data/trainer-names"; import { trainerNamePools } from "#app/data/trainer-names";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Moves } from "#enums/moves";
import { speciesEggMoves } from "#app/data/egg-moves";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:globalTradeSystem"; const namespace = "mysteryEncounter:globalTradeSystem";
@ -222,21 +225,48 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms) // Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) { if (!tradePokemon.shiny) {
// 512/65536 -> 1/128 const baseShinyChance = 512;
tradePokemon.trySetShinySeed(512, true); const shinyThreshold = new Utils.IntegerHolder(baseShinyChance);
if (scene.eventManager.isEventActive()) {
shinyThreshold.value *= scene.eventManager.getShinyMultiplier();
}
scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
// Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms
// Maximum shiny chance of 4090/65536 -> 1/16, cannot improve further after that
const shinyChance = Math.min(shinyThreshold.value, 4090);
tradePokemon.trySetShinySeed(shinyChance, false);
} }
// Extra HA roll at base 1/64 odds (boosted by events and charms) // Extra HA roll at base 1/64 odds (boosted by events and charms)
if (pokemon.species.abilityHidden) { const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
const hiddenIndex = pokemon.species.ability2 ? 2 : 1; if (tradePokemon.species.abilityHidden) {
if (pokemon.abilityIndex < hiddenIndex) { if (tradePokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(64); const hiddenAbilityChance = new IntegerHolder(64);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
if (hasHiddenAbility) { if (hasHiddenAbility) {
pokemon.abilityIndex = hiddenIndex; tradePokemon.abilityIndex = hiddenIndex;
}
}
}
// If Pokemon is still not shiny or with HA, give the Pokemon a random Common egg move in its moveset
if (!tradePokemon.shiny && (!tradePokemon.species.abilityHidden || tradePokemon.abilityIndex < hiddenIndex)) {
const eggMoves: Moves[] = speciesEggMoves[tradePokemon.getSpeciesForm().getRootSpeciesId()];
if (eggMoves) {
// Cannot gen the rare egg move, only 1 of the first 3 common moves
const eggMove = eggMoves[randSeedInt(3)];
if (!tradePokemon.moveset.some(m => m?.moveId === eggMove)) {
if (tradePokemon.moveset.length < 4) {
tradePokemon.moveset.push(new PokemonMove(eggMove));
} else {
const eggMoveIndex = randSeedInt(4);
tradePokemon.moveset[eggMoveIndex] = new PokemonMove(eggMove);
}
} }
} }
} }

View File

@ -33,7 +33,7 @@ const BST_INCREASE_VALUE = 10;
*/ */
export const TheStrongStuffEncounter: MysteryEncounter = export const TheStrongStuffEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
.withEncounterTier(MysteryEncounterTier.GREAT) .withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party .withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
.withMaxAllowedEncounters(1) .withMaxAllowedEncounters(1)

View File

@ -16,7 +16,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
@ -24,6 +23,9 @@ const namespace = "mysteryEncounter:trashToTreasure";
const SOUND_EFFECT_WAIT_TIME = 700; const SOUND_EFFECT_WAIT_TIME = 700;
// Items will cost 2.5x as much for remainder of the run
const SHOP_ITEM_COST_MULTIPLIER = 2.5;
/** /**
* Trash to Treasure encounter. * Trash to Treasure encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3809 | GitHub Issue #3809} * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3809 | GitHub Issue #3809}
@ -79,6 +81,8 @@ export const TrashToTreasureEncounter: MysteryEncounter =
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav"); scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav"); scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
encounter.setDialogueToken("costMultiplier", SHOP_ITEM_COST_MULTIPLIER.toString());
return true; return true;
}) })
.withOption( .withOption(
@ -102,8 +106,14 @@ export const TrashToTreasureEncounter: MysteryEncounter =
transitionMysteryEncounterIntroVisuals(scene); transitionMysteryEncounterIntroVisuals(scene);
await tryApplyDigRewardItems(scene); await tryApplyDigRewardItems(scene);
// Give the player the Black Sludge curse const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [SHOP_ITEM_COST_MULTIPLIER]);
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE)); const modifier = blackSludge?.newModifier();
if (modifier) {
await scene.addModifier(modifier, false, false, false, true);
scene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: modifier.type.name }), null, undefined, true);
}
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(scene, true);
}) })
.build() .build()

View File

@ -429,7 +429,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
} }
function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies { function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies {
let newSpecies: PokemonSpecies | undefined = undefined; let newSpecies: PokemonSpecies | undefined;
while (isNullOrUndefined(newSpecies)) { while (isNullOrUndefined(newSpecies)) {
const bstCap = originalBst + bstSearchRange[1]; const bstCap = originalBst + bstSearchRange[1];
const bstMin = Math.max(originalBst + bstSearchRange[0], 0); const bstMin = Math.max(originalBst + bstSearchRange[0], 0);

View File

@ -32,6 +32,7 @@ import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fu
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter"; import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter"; import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
import { getBiomeName } from "#app/data/biomes";
/** /**
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT * Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
@ -362,11 +363,16 @@ export function initMysteryEncounters() {
}); });
// Add ANY biome encounters to biome map // Add ANY biome encounters to biome map
mysteryEncountersByBiome.forEach(biomeEncounters => { let encounterBiomeTableLog = "";
mysteryEncountersByBiome.forEach((biomeEncounters, biome) => {
anyBiomeEncounters.forEach(encounter => { anyBiomeEncounters.forEach(encounter => {
if (!biomeEncounters.includes(encounter)) { if (!biomeEncounters.includes(encounter)) {
biomeEncounters.push(encounter); biomeEncounters.push(encounter);
} }
}); });
encounterBiomeTableLog += `${getBiomeName(biome).toUpperCase()}: [${biomeEncounters.map(type => MysteryEncounterType[type].toString().toLowerCase()).sort().join(", ")}]\n`;
}); });
console.debug("All Mystery Encounters by Biome:\n" + encounterBiomeTableLog);
} }

View File

@ -31,7 +31,7 @@
"option": { "option": {
"1": { "1": {
"label": "Accept the Challenge", "label": "Accept the Challenge",
"tooltip": "(-) Tough Battle\n(+) Gain a @[TOOLTIP_TITLE]{Very Rare Egg}" "tooltip": "(-) Extremely Tough Battle\n(+) Gain a @[TOOLTIP_TITLE]{Very Rare Egg}"
}, },
"2": { "2": {
"label": "Refuse the Challenge", "label": "Refuse the Challenge",

View File

@ -8,7 +8,7 @@
"option": { "option": {
"1": { "1": {
"label": "Battle the Clown", "label": "Battle the Clown",
"tooltip": "(-) Strange Battle\n(?) Affects Pokémon Abilities", "tooltip": "(-) Strange Battle\n(?) Affects One Pokémon's Ability",
"selected": "Your pitiful Pokémon are poised for a pathetic performance!", "selected": "Your pitiful Pokémon are poised for a pathetic performance!",
"apply_ability_dialogue": "A sensational showcase!\nYour savvy suits a special skill as spoils!", "apply_ability_dialogue": "A sensational showcase!\nYour savvy suits a special skill as spoils!",
"apply_ability_message": "The clown is offering to permanently Skill Swap one of your Pokémon's ability to {{ability}}!", "apply_ability_message": "The clown is offering to permanently Skill Swap one of your Pokémon's ability to {{ability}}!",
@ -17,14 +17,14 @@
}, },
"2": { "2": {
"label": "Remain Unprovoked", "label": "Remain Unprovoked",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Items", "tooltip": "(?) Affects One Pokémon's Items",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!", "selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's {{blacephalonName}} uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!", "selected_2": "The clown's {{blacephalonName}} uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!" "selected_3": "Flustered fool, fall for my flawless deception!"
}, },
"3": { "3": {
"label": "Return the Insults", "label": "Return the Insults",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Types", "tooltip": "(?) Affects Your Pokémons' Types",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!", "selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's {{blacephalonName}} uses a strange move!\nAll of your team's types were randomly swapped!", "selected_2": "The clown's {{blacephalonName}} uses a strange move!\nAll of your team's types were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!" "selected_3": "Flustered fool, fall for my flawless deception!"

View File

@ -1,7 +1,7 @@
{ {
"intro": "A pack of {{delibirdName}} have appeared!", "intro": "A flock of {{delibirdName}} have appeared!",
"title": "Delibir-dy", "title": "Delibir-dy",
"description": "The {{delibirdName}}s are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?", "description": "The {{delibirdName}}s are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?",
"query": "What will you give them?", "query": "What will you give them?",

View File

@ -11,13 +11,13 @@
}, },
"2": { "2": {
"label": "Wonder Trade", "label": "Wonder Trade",
"tooltip": "(+) Send one of your Pokémon to the GTS and get a random Pokémon in return" "tooltip": "(+) Send one of your Pokémon to the GTS and get a random special Pokémon in return"
}, },
"3": { "3": {
"label": "Trade an Item", "label": "Trade an Item",
"trade_options_prompt": "Select an item to send.", "trade_options_prompt": "Select an item to send.",
"invalid_selection": "This Pokémon doesn't have legal items to trade.", "invalid_selection": "This Pokémon doesn't have legal items to trade.",
"tooltip": "(+) Send one of your Items to the GTS and get a random new Item" "tooltip": "(+) Send one of your Items to the GTS and get a random improved Item"
}, },
"4": { "4": {
"label": "Leave", "label": "Leave",

View File

@ -3,8 +3,8 @@
"speaker": "Gentleman", "speaker": "Gentleman",
"intro_dialogue": "Hello there! Have I got a deal just for YOU!", "intro_dialogue": "Hello there! Have I got a deal just for YOU!",
"title": "The Pokémon Salesman", "title": "The Pokémon Salesman",
"description": "\"This {{purchasePokemon}} is extremely unique and carries an ability not normally found in its species! I'll let you have this swell {{purchasePokemon}} for just {{price, money}}!\"\n\n\"What do you say?\"", "description": "\"This {{purchasePokemon}} is extremely unique and @[TOOLTIP_TITLE]{carries an ability not normally found in its species}! I'll let you have this swell {{purchasePokemon}} for just {{price, money}}!\"\n\n\"What do you say?\"",
"description_shiny": "\"This {{purchasePokemon}} is extremely unique and has a pigment not normally found in its species! I'll let you have this swell {{purchasePokemon}} for just {{price, money}}!\"\n\n\"What do you say?\"", "description_shiny": "\"This {{purchasePokemon}} is extremely unique and @[TOOLTIP_TITLE]{has a pigment not normally found in its species}! I'll let you have this swell {{purchasePokemon}} for just {{price, money}}!\"\n\n\"What do you say?\"",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {

View File

@ -1,24 +1,24 @@
{ {
"intro": "You've come across some\ntraining tools and supplies.", "intro": "You've come across some\ntraining tools and supplies.",
"title": "Training Session", "title": "Training Session",
"description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.", "description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by @[TOOLTIP_TITLE]{battling and defeating it with the rest of your team}.",
"query": "How should you train?", "query": "How should you train?",
"invalid_selection": "Pokémon must be healthy enough.", "invalid_selection": "Pokémon must be healthy enough.",
"option": { "option": {
"1": { "1": {
"label": "Light Training", "label": "Light Training",
"tooltip": "(-) Light Battle\n(+) Improve 2 Random IVs of Pokémon", "tooltip": "(-) Light Battle with Chosen Pokémon\n(+) Permanently Improve 2 Random IVs of Chosen Pokémon",
"finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its {{stat1}} and {{stat2}} IVs were improved!" "finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its {{stat1}} and {{stat2}} IVs were improved!"
}, },
"2": { "2": {
"label": "Moderate Training", "label": "Moderate Training",
"tooltip": "(-) Moderate Battle\n(+) Change Pokémon's Nature", "tooltip": "(-) Moderate Battle with Chosen Pokémon\n(+) Permanently Change Chosen Pokémon's Nature",
"select_prompt": "Select a new nature\nto train your Pokémon in.", "select_prompt": "Select a new nature\nto train your Pokémon in.",
"finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its nature was changed to {{nature}}!" "finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its nature was changed to {{nature}}!"
}, },
"3": { "3": {
"label": "Heavy Training", "label": "Heavy Training",
"tooltip": "(-) Harsh Battle\n(+) Change Pokémon's Ability", "tooltip": "(-) Harsh Battle with Chosen Pokémon\n(+) Permanently Change Chosen Pokémon's Ability",
"select_prompt": "Select a new ability\nto train your Pokémon in.", "select_prompt": "Select a new ability\nto train your Pokémon in.",
"finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its ability was changed to {{ability}}!" "finished": "{{selectedPokemon}} returns, feeling\nworn out but accomplished!$Its ability was changed to {{ability}}!"
}, },

View File

@ -6,7 +6,7 @@
"option": { "option": {
"1": { "1": {
"label": "Dig for Valuables", "label": "Dig for Valuables",
"tooltip": "(-) Items in Shops Cost 3x\n(+) Gain Amazing Items", "tooltip": "(-) Items in Shops Will Cost {{costMultiplier}}x\n(+) Gain Amazing Items",
"selected": "You wade through the garbage pile, becoming mired in filth.$There's no way any respectable shopkeeper would\nsell you items at the normal rate in your grimy state!$You'll have to pay extra for items now.$However, you found some incredible items in the garbage!" "selected": "You wade through the garbage pile, becoming mired in filth.$There's no way any respectable shopkeeper would\nsell you items at the normal rate in your grimy state!$You'll have to pay extra for items now.$However, you found some incredible items in the garbage!"
}, },
"2": { "2": {

View File

@ -1577,17 +1577,22 @@ export const modifierTypes = {
MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
return new PokemonBaseStatTotalModifierType(pregenArgs[0] as integer); return new PokemonBaseStatTotalModifierType(pregenArgs[0] as number);
} }
return new PokemonBaseStatTotalModifierType(Utils.randSeedInt(20)); return new PokemonBaseStatTotalModifierType(Utils.randSeedInt(20));
}), }),
MYSTERY_ENCOUNTER_OLD_GATEAU: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { MYSTERY_ENCOUNTER_OLD_GATEAU: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
return new PokemonBaseStatFlatModifierType(pregenArgs[0] as integer, pregenArgs[1] as Stat[]); return new PokemonBaseStatFlatModifierType(pregenArgs[0] as number, pregenArgs[1] as Stat[]);
} }
return new PokemonBaseStatFlatModifierType(Utils.randSeedInt(20), [Stat.HP, Stat.ATK, Stat.DEF]); return new PokemonBaseStatFlatModifierType(Utils.randSeedInt(20), [Stat.HP, Stat.ATK, Stat.DEF]);
}), }),
MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new Modifiers.HealShopCostModifier(type)), MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) {
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new Modifiers.HealShopCostModifier(type, pregenArgs[0] as number));
}
return new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_BLACK_SLUDGE", "black_sludge", (type, _args) => new Modifiers.HealShopCostModifier(type, 2.5));
}),
MYSTERY_ENCOUNTER_MACHO_BRACE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_MACHO_BRACE", "macho_brace", (type, args) => new Modifiers.PokemonIncrementingStatModifier(type, (args[0] as Pokemon).id)), MYSTERY_ENCOUNTER_MACHO_BRACE: () => new PokemonHeldItemModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_MACHO_BRACE", "macho_brace", (type, args) => new Modifiers.PokemonIncrementingStatModifier(type, (args[0] as Pokemon).id)),
MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET", "golden_net", (type, _args) => new Modifiers.BoostBugSpawnModifier(type)), MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new ModifierType("modifierType:ModifierType.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET", "golden_net", (type, _args) => new Modifiers.BoostBugSpawnModifier(type)),
}; };

View File

@ -2576,8 +2576,12 @@ export class LockModifierTiersModifier extends PersistentModifier {
* Black Sludge item * Black Sludge item
*/ */
export class HealShopCostModifier extends PersistentModifier { export class HealShopCostModifier extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) { public readonly shopMultiplier: number;
constructor(type: ModifierType, shopMultiplier: number, stackCount?: integer) {
super(type, stackCount); super(type, stackCount);
this.shopMultiplier = shopMultiplier;
} }
match(modifier: Modifier): boolean { match(modifier: Modifier): boolean {
@ -2585,11 +2589,11 @@ export class HealShopCostModifier extends PersistentModifier {
} }
clone(): HealShopCostModifier { clone(): HealShopCostModifier {
return new HealShopCostModifier(this.type, this.stackCount); return new HealShopCostModifier(this.type, this.shopMultiplier, this.stackCount);
} }
apply(args: any[]): boolean { apply(args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value *= Math.pow(3, this.getStackCount()); (args[0] as Utils.IntegerHolder).value *= this.shopMultiplier;
return true; return true;
} }
@ -2608,7 +2612,7 @@ export class BoostBugSpawnModifier extends PersistentModifier {
return modifier instanceof BoostBugSpawnModifier; return modifier instanceof BoostBugSpawnModifier;
} }
clone(): HealShopCostModifier { clone(): BoostBugSpawnModifier {
return new BoostBugSpawnModifier(this.type, this.stackCount); return new BoostBugSpawnModifier(this.type, this.stackCount);
} }

View File

@ -11,7 +11,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter"; import { DelibirdyEncounter } from "#app/data/mystery-encounters/encounters/delibirdy-encounter";
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { BerryModifier, HealingBoosterModifier, HiddenAbilityRateBoosterModifier, HitHealModifier, LevelIncrementBoosterModifier, PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier } from "#app/modifier/modifier"; import { BerryModifier, HealingBoosterModifier, HitHealModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonInstantReviveModifier, PokemonNatureWeightModifier, PreserveBerryModifier } from "#app/modifier/modifier";
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
@ -104,35 +104,35 @@ describe("Delibird-y - Mystery Encounter", () => {
expect(scene.money).toBe(initialMoney - price); expect(scene.money).toBe(initialMoney - price);
}); });
it("Should give the player a Hidden Ability Charm", async () => { it("Should give the player an Amulet Coin", async () => {
scene.money = 200000; scene.money = 200000;
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
const itemModifier = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier) as HiddenAbilityRateBoosterModifier; const itemModifier = scene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier;
expect(itemModifier).toBeDefined(); expect(itemModifier).toBeDefined();
expect(itemModifier?.stackCount).toBe(1); expect(itemModifier?.stackCount).toBe(1);
}); });
it("Should give the player a Shell Bell if they have max stacks of Berry Pouches", async () => { it("Should give the player a Shell Bell if they have max stacks of Amulet Coins", async () => {
scene.money = 200000; scene.money = 200000;
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
// 5 Healing Charms // Max Amulet Coins
scene.modifiers = []; scene.modifiers = [];
const abilityCharm = generateModifierType(scene, modifierTypes.ABILITY_CHARM)!.newModifier() as HiddenAbilityRateBoosterModifier; const amuletCoin = generateModifierType(scene, modifierTypes.AMULET_COIN)!.newModifier() as MoneyMultiplierModifier;
abilityCharm.stackCount = 4; amuletCoin.stackCount = 5;
await scene.addModifier(abilityCharm, true, false, false, true); await scene.addModifier(amuletCoin, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
const abilityCharmAfter = scene.findModifier(m => m instanceof HiddenAbilityRateBoosterModifier); const amuletCoinAfter = scene.findModifier(m => m instanceof MoneyMultiplierModifier);
const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier); const shellBellAfter = scene.findModifier(m => m instanceof HitHealModifier);
expect(abilityCharmAfter).toBeDefined(); expect(amuletCoinAfter).toBeDefined();
expect(abilityCharmAfter?.stackCount).toBe(4); expect(amuletCoinAfter?.stackCount).toBe(5);
expect(shellBellAfter).toBeDefined(); expect(shellBellAfter).toBeDefined();
expect(shellBellAfter?.stackCount).toBe(1); expect(shellBellAfter?.stackCount).toBe(1);
}); });

View File

@ -70,7 +70,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF); expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF);
expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT); expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
expect(TheStrongStuffEncounter.dialogue).toBeDefined(); expect(TheStrongStuffEncounter.dialogue).toBeDefined();
expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]); expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]);
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`); expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);