add shuckle juice modifier

This commit is contained in:
ImperialSympathizer 2024-07-20 17:31:26 -04:00
parent 4afcdad3db
commit f2b3a9ba4c
18 changed files with 3493 additions and 3055 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

View File

@ -1,11 +1,11 @@
{ {
"textures": [ "textures": [
{ {
"image": "berry_juice.png", "image": "pokemon_salesman.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 24, "w": 40,
"h": 23 "h": 80
}, },
"scale": 1, "scale": 1,
"frames": [ "frames": [
@ -14,20 +14,20 @@
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": true,
"sourceSize": { "sourceSize": {
"w": 24, "w": 80,
"h": 24 "h": 80
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 1, "x": 21,
"y": 2, "y": 2,
"w": 22, "w": 38,
"h": 21 "h": 78
}, },
"frame": { "frame": {
"x": 1, "x": 1,
"y": 1, "y": 1,
"w": 22, "w": 38,
"h": 21 "h": 78
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:04685a0eb6ef9095824b65408ec1b38f:9891674d538df100fcddde29330c21ae:927f117bdb1c2a27226a5540ce00ee8b$" "smartupdate": "$TexturePacker:SmartUpdate:dd57e3db21f3933c15be65bec261f4c1:05c7ef32252a5c2d3ad007b7e26fabd7:ae82f52e471ed81e2558206f05476cd7$"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

@ -87,6 +87,7 @@ export const FightOrFlightEncounter: IMysteryEncounter =
y: -5, y: -5,
scale: 0.75, scale: 0.75,
isItem: true, isItem: true,
disableAnimation: true
}, },
{ {
spriteKey: bossSpriteKey, spriteKey: bossSpriteKey,

View File

@ -0,0 +1,291 @@
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { StatusEffect } from "#app/data/status-effect";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
import { modifierTypes } from "#app/modifier/modifier-type";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "../../../battle-scene";
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { MoneyRequirement } from "../mystery-encounter-requirements";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { applyDamageToPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
import { Species } from "#enums/species";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:pokemonSalesman";
/**
* Pokemon Salesman encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/36 | GitHub Issue #36}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const PokemonSalesmanEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.POKEMON_SALESMAN)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(10, 180)
.withSceneRequirement(new MoneyRequirement(null, 8)) // Some costs may not be as significant, this is the max you'd pay
.withIntroSpriteConfigs([
{
spriteKey: "pokemon_salesman",
fileRoot: "mystery-encounters",
hasShadow: true
}
])
.withIntroDialogue([
{
text: `${namespace}:intro`,
},
{
text: `${namespace}:intro_dialogue`,
speaker: `${namespace}:speaker`,
},
])
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter;
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
const tries = 0;
// Reroll any species that don't have HAs
while (isNullOrUndefined(species.abilityHidden) && tries < 5) {
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
}
let pokemon;
if (isNullOrUndefined(species.abilityHidden) || randSeedInt(100) === 0) {
// If no HA mon found or you roll 1%, give shiny Magikarp
species = getPokemonSpecies(Species.MAGIKARP);
const hiddenIndex = species.ability2 ? 2 : 1;
pokemon = scene.addPlayerPokemon(species, 5, hiddenIndex, species.formIndex, null, true);
} else {
const hiddenIndex = species.ability2 ? 2 : 1;
pokemon = scene.addPlayerPokemon(species, 5, hiddenIndex, species.formIndex);
}
const starterTier = speciesStarters[species.speciesId];
// Prices by starter tier: 8/6.4/4.8/4/4
let priceMultiplier = 8 * (Math.max(starterTier, 2.5) / 5);
if (pokemon.shiny) {
// Always max price for shiny, and add special message to intro
priceMultiplier = 8;
encounter.setDialogueToken("specialShinyText", `$t(${namespace}:shiny)`);
} else {
encounter.setDialogueToken("specialShinyText", "");
}
encounter.setDialogueToken("purchasePokemon", pokemon.name);
encounter.setDialogueToken("price", pokemon.name);
encounter.misc = {
money: scene.getWaveMoneyAmount(priceMultiplier),
pokemon: pokemon,
// shiny: pokemon.shiny
};
pokemon.calculateStats();
return true;
})
.withSimpleOption({
buttonLabel: `${namespace}:option:1:label`,
buttonTooltip: `${namespace}:option:1:tooltip`,
selected: [
{
text: `${namespace}:option:selected`,
},
],
},
async (scene: BattleScene) => {
// Choose Cheap Option
const encounter = scene.currentBattle.mysteryEncounter;
const cost = encounter.misc.money;
// const purchasedPokemon = encounter.misc.pokemon;
// Update money
updatePlayerMoney(scene, -cost);
leaveEncounterWithoutBattle(scene);
})
.withOption(
new MysteryEncounterOptionBuilder()
.withOptionMode(EncounterOptionMode.DEFAULT)
.withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2
.withDialogue({
buttonLabel: `${namespace}:option:1:label`,
buttonTooltip: `${namespace}:option:1:tooltip`,
selected: [
{
text: `${namespace}:option:selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Update money
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
// Calculate modifiers and dialogue tokens
const modifiers = [
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
];
encounter.setDialogueToken("boost1", modifiers[0].name);
encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = {
chosenPokemon: pokemon,
modifiers: modifiers,
};
};
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon meets primary pokemon reqs, it can be selected
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`);
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
// Choose Cheap Option
const encounter = scene.currentBattle.mysteryEncounter;
const chosenPokemon = encounter.misc.chosenPokemon;
const modifiers = encounter.misc.modifiers;
for (const modType of modifiers) {
const modifier = modType.newModifier(chosenPokemon);
await scene.addModifier(modifier, true, false, false, true);
}
scene.updateModifiers(true);
leaveEncounterWithoutBattle(scene);
})
.withPostOptionPhase(async (scene: BattleScene) => {
// Damage and status applied after dealer leaves (to make thematic sense)
const encounter = scene.currentBattle.mysteryEncounter;
const chosenPokemon = encounter.misc.chosenPokemon;
// Pokemon takes 1/3 max HP damage
applyDamageToPokemon(scene, chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 3));
// Roll for poison (80%)
if (randSeedInt(10) < 8) {
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
// Toxic applied
queueEncounterMessage(scene, `${namespace}:bad_poison`);
} else {
// Pokemon immune or something else prevents status
queueEncounterMessage(scene, `${namespace}:damage_only`);
}
} else {
queueEncounterMessage(scene, `${namespace}:damage_only`);
}
setEncounterExp(scene, [chosenPokemon.id], 100);
chosenPokemon.updateInfo();
})
.build()
)
.withOption(
new MysteryEncounterOptionBuilder()
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5
.withDialogue({
buttonLabel: `${namespace}:option:2:label`,
buttonTooltip: `${namespace}:option:2:tooltip`,
selected: [
{
text: `${namespace}:option:selected`,
},
],
})
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter;
const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Update money
updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
// Calculate modifiers and dialogue tokens
const modifiers = [
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
];
encounter.setDialogueToken("boost1", modifiers[0].name);
encounter.setDialogueToken("boost2", modifiers[1].name);
encounter.misc = {
chosenPokemon: pokemon,
modifiers: modifiers,
};
};
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
const selectableFilter = (pokemon: Pokemon) => {
// If pokemon meets primary pokemon reqs, it can be selected
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`);
}
return null;
};
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
})
.withOptionPhase(async (scene: BattleScene) => {
// Choose Expensive Option
const encounter = scene.currentBattle.mysteryEncounter;
const chosenPokemon = encounter.misc.chosenPokemon;
const modifiers = encounter.misc.modifiers;
for (const modType of modifiers) {
const modifier = modType.newModifier(chosenPokemon);
await scene.addModifier(modifier, true, false, false, true);
}
scene.updateModifiers(true);
leaveEncounterWithoutBattle(scene);
})
.withPostOptionPhase(async (scene: BattleScene) => {
// Status applied after dealer leaves (to make thematic sense)
const encounter = scene.currentBattle.mysteryEncounter;
const chosenPokemon = encounter.misc.chosenPokemon;
// Roll for poison (20%)
if (randSeedInt(10) < 2) {
if (chosenPokemon.trySetStatus(StatusEffect.POISON)) {
// Poison applied
queueEncounterMessage(scene, `${namespace}:poison`);
} else {
// Pokemon immune or something else prevents status
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
}
} else {
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
}
setEncounterExp(scene, [chosenPokemon.id], 100);
chosenPokemon.updateInfo();
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}:option:3:label`,
buttonTooltip: `${namespace}:option:3:tooltip`,
},
async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.build();

View File

@ -19,6 +19,11 @@ import { BerryType } from "#enums/berry-type";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theStrongStuff"; const namespace = "mysteryEncounter:theStrongStuff";
/**
* Pokemon Salesman encounter.
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/54 | GitHub Issue #54}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const TheStrongStuffEncounter: IMysteryEncounter = export const TheStrongStuffEncounter: IMysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
.withEncounterTier(MysteryEncounterTier.COMMON) .withEncounterTier(MysteryEncounterTier.COMMON)
@ -28,12 +33,13 @@ export const TheStrongStuffEncounter: IMysteryEncounter =
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "berry_juice", spriteKey: "berry_juice",
fileRoot: "mystery-encounters", fileRoot: "items",
hasShadow: true, hasShadow: true,
isItem: true,
scale: 1.5, scale: 1.5,
x: -15, x: -15,
y: 3, y: 3,
yShadow: 0 disableAnimation: true
}, },
{ {
spriteKey: Species.SHUCKLE.toString(), spriteKey: Species.SHUCKLE.toString(),

View File

@ -14,6 +14,7 @@ import IMysteryEncounter from "./mystery-encounter";
import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter"; import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter"; import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter"; import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";
import { PokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/pokemon-salesman-encounter";
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256 // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
@ -132,14 +133,14 @@ const nonExtremeBiomeEncounters: MysteryEncounterType[] = [
const humanTransitableBiomeEncounters: MysteryEncounterType[] = [ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [
MysteryEncounterType.MYSTERIOUS_CHALLENGERS, MysteryEncounterType.MYSTERIOUS_CHALLENGERS,
MysteryEncounterType.SHADY_VITAMIN_DEALER MysteryEncounterType.SHADY_VITAMIN_DEALER,
MysteryEncounterType.POKEMON_SALESMAN
]; ];
const civilizationBiomeEncounters: MysteryEncounterType[] = [ const civilizationBiomeEncounters: MysteryEncounterType[] = [
MysteryEncounterType.DEPARTMENT_STORE_SALE MysteryEncounterType.DEPARTMENT_STORE_SALE
]; ];
/** /**
* To add an encounter to every biome possible, use this array * To add an encounter to every biome possible, use this array
*/ */
@ -225,6 +226,7 @@ export function initMysteryEncounters() {
allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter; allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter;
allMysteryEncounters[MysteryEncounterType.FIERY_FALLOUT] = FieryFalloutEncounter; allMysteryEncounters[MysteryEncounterType.FIERY_FALLOUT] = FieryFalloutEncounter;
allMysteryEncounters[MysteryEncounterType.THE_STRONG_STUFF] = TheStrongStuffEncounter; allMysteryEncounters[MysteryEncounterType.THE_STRONG_STUFF] = TheStrongStuffEncounter;
allMysteryEncounters[MysteryEncounterType.POKEMON_SALESMAN] = PokemonSalesmanEncounter;
// Add extreme encounters to biome map // Add extreme encounters to biome map
extremeBiomeEncounters.forEach(encounter => { extremeBiomeEncounters.forEach(encounter => {

View File

@ -17,6 +17,7 @@ import { Type } from "#app/data/type";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { modifierTypes } from "#app/modifier/modifier-type";
export interface MysteryEncounterPokemonData { export interface MysteryEncounterPokemonData {
spriteScale?: number spriteScale?: number
@ -204,13 +205,11 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
* @param pokemon * @param pokemon
* @param value * @param value
*/ */
export function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) { export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
pokemon.getSpeciesForm().baseStats = [...pokemon.getSpeciesForm().baseStats].map(v => { const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE().generateType(null, [value]);
const newVal = Math.floor(v + value); const modifier = modType.newModifier(pokemon);
return Math.max(newVal, 1); await pokemon.scene.addModifier(modifier, false, false, false, true);
});
pokemon.calculateStats(); pokemon.calculateStats();
pokemon.updateInfo();
} }
/** /**
@ -387,7 +386,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
}); });
} }
function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType): Promise<void> { export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, pokeball: Phaser.GameObjects.Sprite, pokeballType: PokeballType): Promise<void> {
scene.unshiftPhase(new VictoryPhase(scene, BattlerIndex.ENEMY)); scene.unshiftPhase(new VictoryPhase(scene, BattlerIndex.ENEMY));
const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm(); const speciesForm = !pokemon.fusionSpecies ? pokemon.getSpeciesForm() : pokemon.getFusionSpeciesForm();

View File

@ -11,5 +11,6 @@ export enum MysteryEncounterType {
SAFARI_ZONE, SAFARI_ZONE,
LOST_AT_SEA, //might be generalized later on LOST_AT_SEA, //might be generalized later on
FIERY_FALLOUT, FIERY_FALLOUT,
THE_STRONG_STUFF THE_STRONG_STUFF,
POKEMON_SALESMAN
} }

View File

@ -10,7 +10,7 @@ import * as Utils from "../utils";
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type"; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
import { getLevelTotalExp } from "../data/exp"; import { getLevelTotalExp } from "../data/exp";
import { Stat } from "../data/pokemon-stat"; import { Stat } from "../data/pokemon-stat";
import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonMultiHitModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, StatBoosterModifier, TerastallizeModifier } from "../modifier/modifier"; import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonMultiHitModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, StatBoosterModifier, TerastallizeModifier, PokemonBaseStatTotalModifier } from "../modifier/modifier";
import { PokeballType } from "../data/pokeball"; import { PokeballType } from "../data/pokeball";
import { Gender } from "../data/gender"; import { Gender } from "../data/gender";
import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims"; import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
@ -731,6 +731,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.stats = [ 0, 0, 0, 0, 0, 0 ]; this.stats = [ 0, 0, 0, 0, 0, 0 ];
} }
const baseStats = this.getSpeciesForm().baseStats.slice(0); const baseStats = this.getSpeciesForm().baseStats.slice(0);
this.scene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats);
if (this.fusionSpecies) { if (this.fusionSpecies) {
const fusionBaseStats = this.getFusionSpeciesForm().baseStats; const fusionBaseStats = this.getFusionSpeciesForm().baseStats;
for (let s = 0; s < this.stats.length; s++) { for (let s = 0; s < this.stats.length; s++) {

View File

@ -66,6 +66,16 @@ export const modifierType: ModifierTypeTranslationEntries = {
"PokemonBaseStatBoosterModifierType": { "PokemonBaseStatBoosterModifierType": {
description: "Increases the holder's base {{statName}} by 10%. The higher your IVs, the higher the stack limit.", description: "Increases the holder's base {{statName}} by 10%. The higher your IVs, the higher the stack limit.",
}, },
"PokemonBaseStatTotalModifierType": {
name: "Shuckle Juice",
description: "{{increaseDecrease}} all of the holder's base stats by {{statValue}}. You were {{blessCurse}} by the Shuckle.",
extra: {
"increase": "Increases",
"decrease": "Decreases",
"blessed": "blessed",
"cursed": "cursed"
},
},
"AllPokemonFullHpRestoreModifierType": { "AllPokemonFullHpRestoreModifierType": {
description: "Restores 100% HP for all Pokémon.", description: "Restores 100% HP for all Pokémon.",
}, },
@ -240,6 +250,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
"ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Full Heal Token", description: "Adds a 2.5% chance every turn to heal a status condition." }, "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Full Heal Token", description: "Adds a 2.5% chance every turn to heal a status condition." },
"ENEMY_ENDURE_CHANCE": { name: "Endure Token" }, "ENEMY_ENDURE_CHANCE": { name: "Endure Token" },
"ENEMY_FUSED_CHANCE": { name: "Fusion Token", description: "Adds a 1% chance that a wild Pokémon will be a fusion." }, "ENEMY_FUSED_CHANCE": { name: "Fusion Token", description: "Adds a 1% chance that a wild Pokémon will be a fusion." },
"MYSTERY_ENCOUNTER_SHUCKLE_JUICE": { name: "Shuckle Juice" },
}, },
SpeciesBoosterItem: { SpeciesBoosterItem: {
"LIGHT_BALL": { name: "Light Ball", description: "It's a mysterious orb that boosts Pikachu's Attack and Sp. Atk stats." }, "LIGHT_BALL": { name: "Light Ball", description: "It's a mysterious orb that boosts Pikachu's Attack and Sp. Atk stats." },

View File

@ -11,6 +11,7 @@ import { shadyVitaminDealerDialogue } from "#app/locales/en/mystery-encounters/s
import { slumberingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/slumbering-snorlax-dialogue"; import { slumberingSnorlaxDialogue } from "#app/locales/en/mystery-encounters/slumbering-snorlax-dialogue";
import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/training-session-dialogue"; import { trainingSessionDialogue } from "#app/locales/en/mystery-encounters/training-session-dialogue";
import { theStrongStuffDialogue } from "#app/locales/en/mystery-encounters/the-strong-stuff-dialogue"; import { theStrongStuffDialogue } from "#app/locales/en/mystery-encounters/the-strong-stuff-dialogue";
import { pokemonSalesmanDialogue } from "#app/locales/en/mystery-encounters/pokemon-salesman-dialogue";
/** /**
* Patterns that can be used: * Patterns that can be used:
@ -46,4 +47,5 @@ export const mysteryEncounter = {
lostAtSea: lostAtSeaDialogue, lostAtSea: lostAtSeaDialogue,
fieryFallout: fieryFalloutDialogue, fieryFallout: fieryFalloutDialogue,
theStrongStuff: theStrongStuffDialogue, theStrongStuff: theStrongStuffDialogue,
pokemonSalesman: pokemonSalesmanDialogue
} as const; } as const;

View File

@ -0,0 +1,25 @@
export const pokemonSalesmanDialogue = {
intro: "A chipper elderly man approaches you.",
speaker: "Gentleman",
intro_dialogue: "Hello there! Have I got a deal just for YOU!{{specialShinyText}}",
title: "The Pokémon Salesman",
description: "\"This {{purchasePokemon}} is extremely unique, and carries an ability not normally found on its species! I'll let you have this swell {{purchasePokemon}} for just {{money, money}}!\"\n\n\"What do you say?\"",
query: "What will you do?",
shiny: "$I have SUPER amazing Pokémon that\nanyone would be dying to get!",
option: {
1: {
label: "Accept",
tooltip: "(-) Pay {{money, money}}\n(+) Gain a {{purchasePokemon}} with its Hidden Ability (also saved in Pokédex data)",
selected_dialogue: `Excellent choice!
$I can see you've a keen eye for business.`,
selected_message: "You paid an outrageous sum and bought the {{purchasePokemon}}.",
selected_dialogue_2: "Oh, yeah...@d{64} Returns not accepted, got that?"
},
2: {
label: "Refuse",
tooltip: "(-) No Rewards",
selected: `No?@d{32} You say no?
$I'm only doing this as a favor to you!`,
},
},
};

View File

@ -621,6 +621,27 @@ export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierT
} }
} }
export class PokemonBaseStatTotalModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
private readonly statModifier: integer;
constructor(statModifier: integer) {
super("modifierType:ModifierType.MYSTERY_ENCOUNTER_SHUCKLE_JUICE", "berry_juice", (_type, args) => new Modifiers.PokemonBaseStatTotalModifier(this, (args[0] as Pokemon).id, this.statModifier));
this.statModifier = statModifier;
}
getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonBaseStatTotalModifierType.description", {
increaseDecrease: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.increase" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.decrease"),
blessCurse: i18next.t(this.statModifier >= 0 ? "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.blessed" : "modifierType:ModifierType.PokemonBaseStatTotalModifierType.extra.cursed"),
statValue: this.statModifier,
});
}
getPregenArgs(): any[] {
return [ this.statModifier ];
}
}
class AllPokemonFullHpRestoreModifierType extends ModifierType { class AllPokemonFullHpRestoreModifierType extends ModifierType {
private descriptionKey: string; private descriptionKey: string;
@ -1354,6 +1375,13 @@ export const modifierTypes = {
ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_STATUS_EFFECT_HEAL_CHANCE", "wl_full_heal", (type, _args) => new Modifiers.EnemyStatusEffectHealChanceModifier(type, 2.5, 10)), ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_STATUS_EFFECT_HEAL_CHANCE", "wl_full_heal", (type, _args) => new Modifiers.EnemyStatusEffectHealChanceModifier(type, 2.5, 10)),
ENEMY_ENDURE_CHANCE: () => new EnemyEndureChanceModifierType("modifierType:ModifierType.ENEMY_ENDURE_CHANCE", "wl_reset_urge", 2), ENEMY_ENDURE_CHANCE: () => new EnemyEndureChanceModifierType("modifierType:ModifierType.ENEMY_ENDURE_CHANCE", "wl_reset_urge", 2),
ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new Modifiers.EnemyFusionChanceModifier(type, 1)), ENEMY_FUSED_CHANCE: () => new ModifierType("modifierType:ModifierType.ENEMY_FUSED_CHANCE", "wl_custom_spliced", (type, _args) => new Modifiers.EnemyFusionChanceModifier(type, 1)),
MYSTERY_ENCOUNTER_SHUCKLE_JUICE: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) {
return new PokemonBaseStatTotalModifierType(pregenArgs[0] as integer);
}
return new PokemonBaseStatTotalModifierType(Utils.randSeedInt(20));
}),
}; };
interface ModifierPool { interface ModifierPool {

View File

@ -708,6 +708,55 @@ export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
} }
} }
export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier {
private statModifier: integer;
constructor(type: ModifierTypes.PokemonBaseStatTotalModifierType, pokemonId: integer, statModifier: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
this.statModifier = statModifier;
}
matchType(modifier: Modifier): boolean {
if (modifier instanceof PokemonBaseStatTotalModifier) {
return (modifier as PokemonBaseStatTotalModifier).statModifier === this.statModifier;
}
return false;
}
clone(): PersistentModifier {
return new PokemonBaseStatTotalModifier(this.type as ModifierTypes.PokemonBaseStatTotalModifierType, this.pokemonId, this.statModifier, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.statModifier);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array;
}
apply(args: any[]): boolean {
args[1].forEach((v, i) => {
const newVal = Math.floor(v + this.statModifier);
args[1][i] = Math.min(Math.max(newVal, 1), 999999);
});
return true;
}
getTransferrable(_withinParty: boolean): boolean {
return false;
}
getScoreMultiplier(): number {
return 1.2;
}
getMaxHeldItemCount(pokemon: Pokemon): integer {
return 2;
}
}
/** /**
* Modifier used for held items that apply {@linkcode Stat} boost(s) * Modifier used for held items that apply {@linkcode Stat} boost(s)
* using a multiplier. * using a multiplier.

View File

@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0;
*/ */
// 1 to 256, set to null to ignore // 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_TIER_OVERRIDE: MysteryEncounterTier = null;
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.THE_STRONG_STUFF;
/** /**
* MODIFIER / ITEM OVERRIDES * MODIFIER / ITEM OVERRIDES