Merge branch 'beta' into hebrew-pr

This commit is contained in:
Lugiad 2024-09-25 13:47:14 +02:00 committed by GitHub
commit dccd8aecca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
597 changed files with 11018 additions and 1923 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -2,7 +2,7 @@ import Phaser from "phaser";
import UI from "./ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
import { Constructor, isNullOrUndefined } from "#app/utils";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "./utils";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball";
@ -1201,32 +1201,12 @@ export default class BattleScene extends SceneBase {
// Check for mystery encounter
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
const roll = Utils.randSeedInt(MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT);
// Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents;
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well)
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (newWaveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER;
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
const canSpawn = encounteredEvents.length === 0 || (newWaveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex) > 3 || !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE);
if (canSpawn && roll < successRate) {
if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType) || newBattleType === BattleType.MYSTERY_ENCOUNTER || !isNullOrUndefined(mysteryEncounterType)) {
newBattleType = BattleType.MYSTERY_ENCOUNTER;
// Reset base spawn weight
this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
} else {
this.mysteryEncounterSaveData.encounterSpawnChance = sessionEncounterRate + WEIGHT_INCREMENT_ON_SPAWN_MISS;
}
} else if (newBattleType === BattleType.WILD) {
this.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS;
}
}
@ -1267,9 +1247,8 @@ export default class BattleScene extends SceneBase {
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
this.currentBattle.double = false;
this.executeWithSeedOffset(() => {
this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounterType);
}, this.currentBattle.waveIndex << 4);
// Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome
this.currentBattle.mysteryEncounterType = mysteryEncounterType;
}
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
@ -2657,8 +2636,7 @@ export default class BattleScene extends SceneBase {
modifier = mt.modifier as PokemonHeldItemModifier;
modifier.pokemonId = enemyPokemon.id;
}
const stackCount = mt.stackCount ?? 1;
modifier.stackCount = stackCount;
modifier.stackCount = mt.stackCount ?? 1;
modifier.isTransferable = mt.isTransferable ?? modifier.isTransferable;
this.addEnemyModifier(modifier, true);
});
@ -3097,6 +3075,51 @@ export default class BattleScene extends SceneBase {
}
}
/**
* Determines whether a wave should randomly generate a {@linkcode MysteryEncounter}.
* Currently, the only modes that MEs are allowed in are Classic and Challenge.
* Additionally, MEs cannot spawn outside of waves 10-180 in those modes
*
* @param newBattleType
* @param waveIndex
* @param sessionDataEncounterType
*/
private isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean {
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(waveIndex) && waveIndex < highestMysteryEncounterWave && waveIndex > lowestMysteryEncounterWave) {
// If ME type is already defined in session data, no need to roll RNG check
if (!isNullOrUndefined(sessionDataEncounterType)) {
return true;
}
// Base spawn weight is BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT/256, and increases by WEIGHT_INCREMENT_ON_SPAWN_MISS/256 for each missed attempt at spawning an encounter on a valid floor
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
const encounteredEvents = this.mysteryEncounterSaveData.encounteredEvents;
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well)
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (waveIndex - lowestMysteryEncounterWave);
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER;
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
const canSpawn = encounteredEvents.length === 0 || (waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex) > 3 || !isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE);
if (canSpawn) {
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
// Always rolls the check on the same offset to ensure no RNG changes from reloading session
this.executeWithSeedOffset(() => {
roll = randSeedInt(MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT);
}, waveIndex * 3 * 1000);
return roll < successRate;
}
}
return false;
}
/**
* Loads or generates a mystery encounter
* @param encounterType used to load session encounter when restarting game, etc.
@ -3105,10 +3128,10 @@ export default class BattleScene extends SceneBase {
getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter {
// Loading override or session encounter
let encounter: MysteryEncounter | null;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE!)) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE!];
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) {
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
} else {
encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType!] : null;
encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null;
}
// Check for queued encounters first
@ -3151,7 +3174,7 @@ export default class BattleScene extends SceneBase {
let tier: MysteryEncounterTier | null = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > greatThreshold ? MysteryEncounterTier.GREAT : tierValue > ultraThreshold ? MysteryEncounterTier.ULTRA : MysteryEncounterTier.ROGUE;
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE)) {
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE!;
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE;
}
let availableEncounters: MysteryEncounter[] = [];

View File

@ -18,6 +18,7 @@ import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { CustomModifierSettings } from "#app/modifier/modifier-type";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
export enum ClassicFixedBossWaves {
// TODO: other fixed wave battles should be added here
@ -88,6 +89,7 @@ export default class Battle {
public playerFaintsHistory: FaintLogEntry[] = [];
public enemyFaintsHistory: FaintLogEntry[] = [];
public mysteryEncounterType?: MysteryEncounterType;
/** If the current battle is a Mystery Encounter, this will always be defined */
public mysteryEncounter?: MysteryEncounter;

72
src/data/ability.ts Executable file → Normal file
View File

@ -366,6 +366,10 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
return false;
}
getImmuneType(): Type | null {
return this.immuneType;
}
override getCondition(): AbAttrCondition | null {
return this.condition;
}
@ -1798,6 +1802,61 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
}
}
/**
* Base class for defining all {@linkcode Ability} Attributes after a status effect has been set.
* @see {@linkcode applyPostSetStatus()}.
*/
export class PostSetStatusAbAttr extends AbAttr {
/**
* Does nothing after a status condition is set.
* @param pokemon {@linkcode Pokemon} that status condition was set on.
* @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon.
* @param passive Whether this ability is a passive.
* @param effect {@linkcode StatusEffect} that was set.
* @param args Set of unique arguments needed by this attribute.
* @returns `true` if application of the ability succeeds.
*/
applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]) : boolean | Promise<boolean> {
return false;
}
}
/**
* If another Pokemon burns, paralyzes, poisons, or badly poisons this Pokemon,
* that Pokemon receives the same non-volatile status condition as part of this
* ability attribute. For Synchronize ability.
*/
export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr {
/**
* If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status
* was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`.
* @param pokemon {@linkcode Pokemon} that status condition was set on.
* @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon.
* @param passive Whether this ability is a passive.
* @param effect {@linkcode StatusEffect} that was set.
* @param args Set of unique arguments needed by this attribute.
* @returns `true` if application of the ability succeeds.
*/
override applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): boolean {
/** Synchronizable statuses */
const syncStatuses = new Set<StatusEffect>([
StatusEffect.BURN,
StatusEffect.PARALYSIS,
StatusEffect.POISON,
StatusEffect.TOXIC
]);
if (sourcePokemon && syncStatuses.has(effect)) {
if (!simulated) {
sourcePokemon.trySetStatus(effect, true, pokemon);
}
return true;
}
return false;
}
}
export class PostVictoryAbAttr extends AbAttr {
applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
return false;
@ -4241,6 +4300,10 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr {
}
}
/**
* Ability attribute used for abilites that change the ability owner's weight
* Used for Heavy Metal (doubling weight) and Light Metal (halving weight)
*/
export class WeightMultiplierAbAttr extends AbAttr {
private multiplier: integer;
@ -4677,6 +4740,10 @@ export function applyStatMultiplierAbAttrs(attrType: Constructor<StatMultiplierA
pokemon: Pokemon, stat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<StatMultiplierAbAttr>(attrType, pokemon, (attr, passive) => attr.applyStatStage(pokemon, passive, simulated, stat, statValue, args), args);
}
export function applyPostSetStatusAbAttrs(attrType: Constructor<PostSetStatusAbAttr>,
pokemon: Pokemon, effect: StatusEffect, sourcePokemon?: Pokemon | null, simulated: boolean = false, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), args, false, simulated);
}
/**
* Applies a field Stat multiplier attribute
@ -4907,7 +4974,8 @@ export function initAbilities() {
.attr(EffectSporeAbAttr),
new Ability(Abilities.SYNCHRONIZE, 3)
.attr(SyncEncounterNatureAbAttr)
.unimplemented(),
.attr(SynchronizeStatusAbAttr)
.partial(), // interaction with psycho shift needs work, keeping to old Gen interaction for now
new Ability(Abilities.CLEAR_BODY, 3)
.attr(ProtectStatAbAttr)
.ignorable(),
@ -5879,6 +5947,6 @@ export function initAbilities() {
new Ability(Abilities.POISON_PUPPETEER, 9)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.conditionalAttr(pokemon => pokemon.species.speciesId===Species.PECHARUNT, ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.attr(ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
);
}

View File

@ -430,7 +430,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
scene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon();
if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!);
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
} else if (fieldPokemon?.isOnField()) {
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
}
@ -915,12 +915,12 @@ export abstract class BattleAnim {
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ];
let r = anim!.frames.length; // TODO: is this bang correct?
let r = anim?.frames.length ?? 0;
let f = 0;
scene.tweens.addCounter({
duration: Utils.getFrameMs(3),
repeat: anim!.frames.length, // TODO: is this bang correct?
repeat: anim?.frames.length ?? 0,
onRepeat: () => {
if (!f) {
userSprite.setVisible(false);
@ -1264,7 +1264,7 @@ export class CommonBattleAnim extends BattleAnim {
}
getAnim(): AnimConfig | null {
return this.commonAnim ? commonAnims.get(this.commonAnim)! : null; // TODO: is this bang correct?
return this.commonAnim ? commonAnims.get(this.commonAnim) ?? null : null;
}
isOppAnim(): boolean {
@ -1284,7 +1284,7 @@ export class MoveAnim extends BattleAnim {
getAnim(): AnimConfig {
return moveAnims.get(this.move) instanceof AnimConfig
? moveAnims.get(this.move) as AnimConfig
: moveAnims.get(this.move)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct?
: moveAnims.get(this.move)?.[this.user?.isPlayer() ? 0 : 1] as AnimConfig;
}
isOppAnim(): boolean {
@ -1316,7 +1316,7 @@ export class MoveChargeAnim extends MoveAnim {
getAnim(): AnimConfig {
return chargeAnims.get(this.chargeAnim) instanceof AnimConfig
? chargeAnims.get(this.chargeAnim) as AnimConfig
: chargeAnims.get(this.chargeAnim)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct?
: chargeAnims.get(this.chargeAnim)?.[this.user?.isPlayer() ? 0 : 1] as AnimConfig;
}
}

View File

@ -2281,6 +2281,36 @@ export class TarShotTag extends BattlerTag {
}
}
/**
* Battler Tag that keeps track of how many times the user has Autotomized
* Each count of Autotomization reduces the weight by 100kg
*/
export class AutotomizedTag extends BattlerTag {
public autotomizeCount: number = 0;
constructor(sourceMove: Moves = Moves.AUTOTOMIZE) {
super(BattlerTagType.AUTOTOMIZED, BattlerTagLapseType.CUSTOM, 1, sourceMove);
}
/**
* Adds an autotomize count to the Pokemon. Each stack reduces weight by 100kg
* If the Pokemon is over 0.1kg it also displays a message.
* @param pokemon The Pokemon that is being autotomized
*/
onAdd(pokemon: Pokemon): void {
const minWeight = 0.1;
if (pokemon.getWeight() > minWeight) {
pokemon.scene.queueMessage(i18next.t("battlerTags:autotomizeOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)
}));
}
this.autotomizeCount += 1;
}
onOverlap(pokemon: Pokemon): void {
this.onAdd(pokemon);
}
}
export class SubstituteTag extends BattlerTag {
/** The substitute's remaining HP. If HP is depleted, the Substitute fades. */
public hp: number;
@ -2568,6 +2598,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new GorillaTacticsTag();
case BattlerTagType.SUBSTITUTE:
return new SubstituteTag(sourceMove, sourceId);
case BattlerTagType.AUTOTOMIZED:
return new AutotomizedTag();
case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON:
return new MysteryEncounterPostSummonTag();
case BattlerTagType.HEAL_BLOCK:

View File

@ -172,11 +172,9 @@ export abstract class Challenge {
* @param overrideValue {@link integer} The value to check for. If undefined, gets the current value.
* @returns {@link string} The localised name for the current value.
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return i18next.t(`challenges:${this.geti18nKey()}.value.${this.value}`);
getValue(overrideValue?: number): string {
const value = overrideValue ?? this.value;
return i18next.t(`challenges:${this.geti18nKey()}.value.${value}`);
}
/**
@ -184,11 +182,9 @@ export abstract class Challenge {
* @param overrideValue {@link integer} The value to check for. If undefined, gets the current value.
* @returns {@link string} The localised description for the current value.
*/
getDescription(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${this.value}`, `challenges:${this.geti18nKey()}.desc`])}`;
getDescription(overrideValue?: number): string {
const value = overrideValue ?? this.value;
return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${value}`, `challenges:${this.geti18nKey()}.desc`])}`;
}
/**
@ -451,30 +447,6 @@ export class SingleGenerationChallenge extends Challenge {
applyFixedBattle(waveIndex: Number, battleConfig: FixedBattleConfig): boolean {
let trainerTypes: TrainerType[] = [];
switch (waveIndex) {
case 35:
trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ];
break;
case 62:
trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ];
break;
case 64:
trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ];
break;
case 66:
trainerTypes = [ Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.TABITHA, TrainerType.COURTNEY, TrainerType.MATT, TrainerType.SHELLY ]), Utils.randSeedItem([ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ]), Utils.randSeedItem([ TrainerType.ZINZOLIN, TrainerType.ROOD ]), Utils.randSeedItem([ TrainerType.XEROSIC, TrainerType.BRYONY ]), Utils.randSeedItem([ TrainerType.FABA, TrainerType.PLUMERIA ]), TrainerType.OLEANA, Utils.randSeedItem([ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]) ];
break;
case 112:
trainerTypes = [ TrainerType.ROCKET_GRUNT, TrainerType.ROCKET_GRUNT, Utils.randSeedItem([ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ]), TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, Utils.randSeedItem([ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ]), TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ];
break;
case 114:
trainerTypes = [ Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ]), Utils.randSeedItem([ TrainerType.TABITHA, TrainerType.COURTNEY, TrainerType.MATT, TrainerType.SHELLY ]), Utils.randSeedItem([ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ]), Utils.randSeedItem([ TrainerType.ZINZOLIN, TrainerType.ROOD ]), Utils.randSeedItem([ TrainerType.XEROSIC, TrainerType.BRYONY ]), Utils.randSeedItem([ TrainerType.FABA, TrainerType.PLUMERIA ]), TrainerType.OLEANA, Utils.randSeedItem([ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]) ];
break;
case 115:
trainerTypes = [ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.ROCKET_BOSS_GIOVANNI_1, Utils.randSeedItem([ TrainerType.MAXIE, TrainerType.ARCHIE ]), TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, Utils.randSeedItem([ TrainerType.LUSAMINE, TrainerType.GUZMA ]), TrainerType.ROSE, TrainerType.PENNY ];
break;
case 165:
trainerTypes = [ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.ROCKET_BOSS_GIOVANNI_2, Utils.randSeedItem([ TrainerType.MAXIE_2, TrainerType.ARCHIE_2 ]), TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, Utils.randSeedItem([ TrainerType.LUSAMINE_2, TrainerType.GUZMA_2 ]), TrainerType.ROSE_2, TrainerType.PENNY_2 ];
break;
case 182:
trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]), TrainerType.MARNIE_ELITE, TrainerType.RIKA ];
break;
@ -511,14 +483,12 @@ export class SingleGenerationChallenge extends Challenge {
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised name for the current value.
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
if (this.value === 0) {
getValue(overrideValue?: number): string {
const value = overrideValue ?? this.value;
if (value === 0) {
return i18next.t("settings:off");
}
return i18next.t(`starterSelectUiHandler:gen${this.value}`);
return i18next.t(`starterSelectUiHandler:gen${value}`);
}
/**
@ -526,14 +496,12 @@ export class SingleGenerationChallenge extends Challenge {
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised description for the current value.
*/
getDescription(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
if (this.value === 0) {
getDescription(overrideValue?: number): string {
const value = overrideValue ?? this.value;
if (value === 0) {
return i18next.t("challenges:singleGeneration.desc_default");
}
return i18next.t("challenges:singleGeneration.desc", { gen: i18next.t(`challenges:singleGeneration.gen_${this.value}`) });
return i18next.t("challenges:singleGeneration.desc", { gen: i18next.t(`challenges:singleGeneration.gen_${value}`) });
}

View File

@ -650,7 +650,7 @@ export default class Move implements Localizable {
}
/**
* Applies each {@linkcode MoveCondition} of this move to the params
* Applies each {@linkcode MoveCondition} function of this move to the params, determines if the move can be used prior to calling each attribute's apply()
* @param user {@linkcode Pokemon} to apply conditions to
* @param target {@linkcode Pokemon} to apply conditions to
* @param move {@linkcode Move} to apply conditions to
@ -2091,21 +2091,20 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
if (target.status) {
return false;
}
//@ts-ignore - how can target.status.effect be checked when we return `false` before when it's defined?
if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { // TODO: resolve ts-ignore
const statusAfflictResult = target.trySetStatus(statusToApply, true, user);
if (statusAfflictResult) {
} else {
const canSetStatus = target.canSetStatus(statusToApply, true, false, user);
if (canSetStatus) {
if (user.status) {
user.scene.queueMessage(getStatusEffectHealText(user.status.effect, getPokemonNameWithAffix(user)));
}
user.resetStatus();
user.updateInfo();
}
return statusAfflictResult;
target.trySetStatus(statusToApply, true, user);
}
return false;
return canSetStatus;
}
}
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
@ -5175,30 +5174,28 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
}
export class ForceSwitchOutAttr extends MoveEffectAttr {
private user: boolean;
private batonPass: boolean;
constructor(user?: boolean, batonPass?: boolean) {
constructor(
private selfSwitch: boolean = false,
private batonPass: boolean = false
) {
super(false, MoveEffectTrigger.POST_APPLY, false, true);
this.user = !!user;
this.batonPass = !!batonPass;
}
isBatonPass() {
return this.batonPass;
}
// TODO: Why is this a Promise?
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise(resolve => {
// Check if the move category is not STATUS or if the switch out condition is not met
if (!this.getSwitchOutCondition()(user, target, move)) {
return resolve(false);
}
// Move the switch out logic inside the conditional block
// This ensures that the switch out only happens when the conditions are met
const switchOutTarget = this.user ? user : target;
const switchOutTarget = this.selfSwitch ? user : target;
if (switchOutTarget instanceof PlayerPokemon) {
switchOutTarget.leaveField(!this.batonPass);
@ -5215,7 +5212,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
if (switchOutTarget.hp > 0) {
// for opponent switching out
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, this.batonPass, false), MoveEndPhase);
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(),
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
false, this.batonPass, false), MoveEndPhase);
}
} else {
// Switch out logic for everything else (eg: WILD battles)
@ -5257,29 +5256,33 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
getSwitchOutCondition(): MoveConditionFunc {
return (user, target, move) => {
const switchOutTarget = (this.user ? user : target);
const switchOutTarget = (this.selfSwitch ? user : target);
const player = switchOutTarget instanceof PlayerPokemon;
if (!this.user && move.hitsSubstitute(user, target)) {
if (!this.selfSwitch) {
if (move.hitsSubstitute(user, target)) {
return false;
}
if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr))) {
return false;
const blockedByAbility = new Utils.BooleanHolder(false);
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
return !blockedByAbility.value;
}
if (!player && !user.scene.currentBattle.battleType) {
if (!player && user.scene.currentBattle.battleType === BattleType.WILD) {
if (this.batonPass) {
return false;
}
// Don't allow wild opponents to flee on the boss stage since it can ruin a run early on
if (!(user.scene.currentBattle.waveIndex % 10)) {
if (user.scene.currentBattle.waveIndex % 10 === 0) {
return false;
}
}
const party = player ? user.scene.getParty() : user.scene.getEnemyParty();
return (!player && !user.scene.currentBattle.battleType) || party.filter(p => p.isAllowedInBattle() && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
return (!player && !user.scene.currentBattle.battleType)
|| party.filter(p => p.isAllowedInBattle()
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
};
}
@ -5287,8 +5290,8 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
if (!user.scene.getEnemyParty().find(p => p.isActive() && !p.isOnField())) {
return -20;
}
let ret = this.user ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move);
if (this.user && this.batonPass) {
let ret = this.selfSwitch ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move);
if (this.selfSwitch && this.batonPass) {
const statStageTotal = user.getStatStages().reduce((s: integer, total: integer) => total += s, 0);
ret = ret / 2 + (Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(statStageTotal), 10) / 10) * (statStageTotal >= 0 ? 10 : -10));
}
@ -5296,6 +5299,21 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
}
}
export class ChillyReceptionAttr extends ForceSwitchOutAttr {
// using inherited constructor
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
user.scene.arena.trySetWeather(WeatherType.SNOW, true);
return super.apply(user, target, move, args);
}
getCondition(): MoveConditionFunc {
// chilly reception move will go through if the weather is change-able to snow, or the user can switch out, else move will fail
return (user, target, move) => user.scene.arena.trySetWeather(WeatherType.SNOW, true) || super.getSwitchOutCondition()(user, target, move);
}
}
export class RemoveTypeAttr extends MoveEffectAttr {
private removedType: Type;
@ -8091,7 +8109,7 @@ export function initMoves() {
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1),
new SelfStatusMove(Moves.AUTOTOMIZE, Type.STEEL, -1, 15, -1, 0, 5)
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true)
.partial(),
.attr(AddBattlerTagAttr, BattlerTagType.AUTOTOMIZED, true),
new SelfStatusMove(Moves.RAGE_POWDER, Type.BUG, -1, 20, -1, 2, 5)
.powderMove()
.attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true),
@ -9073,8 +9091,7 @@ export function initMoves() {
new AttackMove(Moves.AURA_WHEEL, Type.ELECTRIC, MoveCategory.PHYSICAL, 110, 100, 10, 100, 0, 8)
.attr(StatStageChangeAttr, [ Stat.SPD ], 1, true)
.makesContact(false)
.attr(AuraWheelTypeAttr)
.condition((user, target, move) => [user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)), // Missing custom fail message
.attr(AuraWheelTypeAttr),
new AttackMove(Moves.BREAKING_SWIPE, Type.DRAGON, MoveCategory.PHYSICAL, 60, 100, 15, 100, 0, 8)
.target(MoveTarget.ALL_NEAR_ENEMIES)
.attr(StatStageChangeAttr, [ Stat.ATK ], -1),
@ -9486,10 +9503,9 @@ export function initMoves() {
.makesContact(),
new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9)
.unimplemented(),
new StatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)
.attr(WeatherChangeAttr, WeatherType.SNOW)
.attr(ForceSwitchOutAttr, true, false)
.target(MoveTarget.BOTH_SIDES),
new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)
.attr(PreMoveMessageAttr, (user, move) => i18next.t("moveTriggers:chillyReception", {pokemonName: getPokemonNameWithAffix(user)}))
.attr(ChillyReceptionAttr, true, false),
new SelfStatusMove(Moves.TIDY_UP, Type.NORMAL, -1, 10, -1, 0, 9)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true, null, true, true)
.attr(RemoveArenaTrapAttr, true)

View File

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

View File

@ -10,7 +10,7 @@ import { PersistentModifierRequirement } from "#app/data/mystery-encounters/myst
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 { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#enums/moves";
import { BattlerTagType } from "#enums/battler-tag-type";
@ -159,12 +159,6 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
])
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withOnVisualsStart((scene: BattleScene) => {
doGreedentSpriteSteal(scene);
doBerrySpritePile(scene);
return true;
})
.withIntroDialogue([
{
text: `${namespace}.intro`,
@ -202,10 +196,15 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [berryMod.berryType]) as PokemonHeldItemModifierType;
bossModifierConfigs.push({ modifier: modifierType });
}
scene.removeModifier(berryMod);
});
// Do NOT remove the real berries yet or else it will be persisted in the session data
// SpDef buff below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
[Stat.SPDEF] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
// Calculate boss mon
const config: EnemyPartyConfig = {
levelAdditiveModifier: 1,
@ -214,12 +213,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
species: getPokemonSpecies(Species.GREEDENT),
isBoss: true,
bossSegments: 3,
moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF],
moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH],
modifierConfigs: bossModifierConfigs,
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}
}
],
@ -230,6 +229,21 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
return true;
})
.withOnVisualsStart((scene: BattleScene) => {
doGreedentSpriteSteal(scene);
doBerrySpritePile(scene);
// Remove the berries from the party
// Session has been safely saved at this point, so data won't be lost
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
berryItems.forEach(berryMod => {
scene.removeModifier(berryMod);
});
scene.updateModifiers(true);
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
@ -251,7 +265,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
const givePartyPokemonReviverSeeds = () => {
const party = scene.getParty();
party.forEach(p => {
if (revSeed) {
const heldItems = p.getHeldItems();
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);

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 { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
@ -17,6 +17,14 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:offerYouCantRefuse";
/**
* Money offered starts at base value of Relic Gold, increasing linearly up to 3x Relic Gold based on the starter tier of the Pokemon being purchased
* Starter value 1-3 -> Relic Gold
* Starter value 10 -> 3 * Relic Gold
*/
const MONEY_MINIMUM_MULTIPLIER = 10;
const MONEY_MAXIMUM_MULTIPLIER = 30;
/**
* An Offer You Can't Refuse encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3808 | GitHub Issue #3808}
@ -61,7 +69,11 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true);
const price = scene.getWaveMoneyAmount(10);
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(true);
const starterValue: number = speciesStarters[baseSpecies] ?? 1;
const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER);
const price = scene.getWaveMoneyAmount(multiplier);
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString());

View File

@ -127,7 +127,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!;
const numBerries = encounter.misc.numBerries;
const doBerryRewards = async () => {
const doBerryRewards = () => {
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
@ -135,7 +135,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) {
await tryGiveBerry(scene);
tryGiveBerry(scene);
}
};
@ -178,7 +178,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
if (speedDiff < 1) {
// Caught and attacked by boss, gets +1 to all stats at start of fight
const doBerryRewards = async () => {
const doBerryRewards = () => {
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
@ -186,15 +186,20 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) {
await tryGiveBerry(scene);
tryGiveBerry(scene);
}
};
// Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
};
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await showEncounterText(scene, `${namespace}.option.2.selected_bad`);
@ -204,7 +209,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1)/0.08), numBerries), 2);
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
const doFasterBerryRewards = async () => {
const doFasterBerryRewards = () => {
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
scene.playSound("item_fanfare");
@ -212,7 +217,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
for (let i = 0; i < numBerriesGrabbed; i++) {
await tryGiveBerry(scene, fastestPokemon);
tryGiveBerry(scene, fastestPokemon);
}
};
@ -242,7 +247,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
)
.build();
async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
@ -254,7 +259,7 @@ async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokem
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
await applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
return;
}
}
@ -265,7 +270,7 @@ async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokem
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
await applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
return;
}
}

View File

@ -492,7 +492,7 @@ function getTrainerConfigForWave(waveIndex: number) {
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
@ -515,14 +515,14 @@ function getTrainerConfigForWave(waveIndex: number) {
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon2.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon2.formIndex)) {
p.formIndex = pool3Mon2.formIndex!;
p.formIndex = pool3Mon2.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
@ -543,7 +543,7 @@ function getTrainerConfigForWave(waveIndex: number) {
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
@ -566,14 +566,14 @@ function getTrainerConfigForWave(waveIndex: number) {
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => {
if (!isNullOrUndefined(pool3Mon.formIndex)) {
p.formIndex = pool3Mon.formIndex!;
p.formIndex = pool3Mon.formIndex;
p.generateAndPopulateMoveset();
p.generateName();
}

View File

@ -10,7 +10,7 @@ import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement }
import { getEncounterText, showEncounterText } 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, 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 { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import i18next from "#app/plugins/i18n";
@ -33,7 +33,7 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
"PokemonBaseStatTotalModifier"
];
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5;
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
/**
* Delibird-y encounter.
@ -122,9 +122,9 @@ export const DelibirdyEncounter: MysteryEncounter =
return true;
})
.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
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)) {
// At max stacks, give the first party pokemon a Shell Bell instead
@ -133,7 +133,7 @@ export const DelibirdyEncounter: MysteryEncounter =
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
} else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ABILITY_CHARM));
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN));
}
leaveEncounterWithoutBattle(scene, true);

View File

@ -63,7 +63,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
// Choose TMs
const modifiers: ModifierTypeFunc[] = [];
let i = 0;
while (i < 4) {
while (i < 5) {
// 2/2/1 weight on TM rarity
const roll = randSeedInt(5);
if (roll < 2) {

View File

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

View File

@ -189,7 +189,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
}
// Burn random member
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.NONE);
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status.effect === StatusEffect.NONE);
if (burnable?.length > 0) {
const roll = randSeedInt(burnable.length);
const chosenPokemon = burnable[roll];

View File

@ -11,9 +11,10 @@ import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon
import { getTypeRgb } from "#app/data/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import * as Utils from "#app/utils";
import { IntegerHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import PokemonData from "#app/system/pokemon-data";
import i18next from "i18next";
@ -28,6 +29,11 @@ import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-en
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:globalTradeSystem";
/** Base shiny chance of 512/65536 -> 1/128 odds, affected by events and Shiny Charms. Cannot exceed 1/16 odds. */
const WONDER_TRADE_SHINY_CHANCE = 512;
/** Max shiny chance of 4096/65536 -> 1/16 odds. */
const MAX_WONDER_TRADE_SHINY_CHANCE = 4096;
const LEGENDARY_TRADE_POOLS = {
1: [Species.RATTATA, Species.PIDGEY, Species.WEEDLE],
2: [Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA],
@ -153,7 +159,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return true;
},
onHover: () => {
const formName = tradePokemon.species.forms?.[pokemon.formIndex]?.formName;
const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[pokemon.formIndex].formName : null;
const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : "");
const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
@ -221,21 +227,47 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) {
// 512/65536 -> 1/128
tradePokemon.trySetShinySeed(512, true);
const shinyThreshold = new Utils.IntegerHolder(WONDER_TRADE_SHINY_CHANCE);
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 4096/65536 -> 1/16, cannot improve further after that
const shinyChance = Math.min(shinyThreshold.value, MAX_WONDER_TRADE_SHINY_CHANCE);
tradePokemon.trySetShinySeed(shinyChance, false);
}
// Extra HA roll at base 1/64 odds (boosted by events and charms)
if (pokemon.species.abilityHidden) {
const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
if (pokemon.abilityIndex < hiddenIndex) {
const hiddenIndex = tradePokemon.species.ability2 ? 2 : 1;
if (tradePokemon.species.abilityHidden) {
if (tradePokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new IntegerHolder(64);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
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 = tradePokemon.getEggMoves();
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);
}
}
}
}
@ -450,7 +482,7 @@ function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?:
if (validSpecies?.length > 20) {
validSpecies = randSeedShuffle(validSpecies);
newSpecies = validSpecies.pop();
while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) {
while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies)) {
newSpecies = validSpecies.pop();
}
} else {

View File

@ -19,10 +19,11 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
const namespace = "mysteryEncounter:mysteriousChest";
const RAND_LENGTH = 100;
const COMMON_REWARDS_WEIGHT = 20; // 20%
const ULTRA_REWARDS_WEIGHT = 50; // 30%
const ROGUE_REWARDS_WEIGHT = 60; // 10%
const MASTER_REWARDS_WEIGHT = 65; // 5%
const TRAP_PERCENT = 35;
const COMMON_REWARDS_PERCENT = 20;
const ULTRA_REWARDS_PERCENT = 30;
const ROGUE_REWARDS_PERCENT = 10;
const MASTER_REWARDS_PERCENT = 5;
/**
* Mysterious Chest encounter.
@ -83,6 +84,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [config];
encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(Species.GIMMIGHOUL).getName());
encounter.setDialogueToken("trapPercent", TRAP_PERCENT.toString());
encounter.setDialogueToken("commonPercent", COMMON_REWARDS_PERCENT.toString());
encounter.setDialogueToken("ultraPercent", ULTRA_REWARDS_PERCENT.toString());
encounter.setDialogueToken("roguePercent", ROGUE_REWARDS_PERCENT.toString());
encounter.setDialogueToken("masterPercent", MASTER_REWARDS_PERCENT.toString());
return true;
})
@ -109,7 +115,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
roll
};
if (roll >= MASTER_REWARDS_WEIGHT) {
if (roll < TRAP_PERCENT) {
// Chest is springing trap, change to red chest sprite
const blueChestSprites = introVisuals.getSpriteAtIndex(0);
const redChestSprites = introVisuals.getSpriteAtIndex(1);
@ -124,7 +130,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
// Open the chest
const encounter = scene.currentBattle.mysteryEncounter!;
const roll = encounter.misc.roll;
if (roll < COMMON_REWARDS_WEIGHT) {
if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) {
// Choose between 2 COMMON / 2 GREAT tier items (20%)
setEncounterRewards(scene, {
guaranteedModifierTiers: [
@ -137,7 +143,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
// Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.normal`);
leaveEncounterWithoutBattle(scene);
} else if (roll < ULTRA_REWARDS_WEIGHT) {
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%)
setEncounterRewards(scene, {
guaranteedModifierTiers: [
@ -149,13 +155,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
// Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.good`);
leaveEncounterWithoutBattle(scene);
} else if (roll < ROGUE_REWARDS_WEIGHT) {
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// Choose between 2 ROGUE tier items (10%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE] });
// Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}.option.1.great`);
leaveEncounterWithoutBattle(scene);
} else if (roll < MASTER_REWARDS_WEIGHT) {
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
// Choose 1 MASTER tier item (5%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER] });
// Display result message then proceed to rewards

View File

@ -27,6 +27,8 @@ const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768];
const SAFARI_MONEY_MULTIPLIER = 2;
const NUM_SAFARI_ENCOUNTERS = 3;
/**
* Safari Zone encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3800 | GitHub Issue #3800}
@ -55,6 +57,10 @@ export const SafariZoneEncounter: MysteryEncounter =
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => {
scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
return true;
})
.withOption(MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneRequirement(new MoneyRequirement(0, SAFARI_MONEY_MULTIPLIER)) // Cost equal to 1 Max Revive
@ -72,7 +78,7 @@ export const SafariZoneEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.continuousEncounter = true;
encounter.misc = {
safariPokemonRemaining: 3
safariPokemonRemaining: NUM_SAFARI_ENCOUNTERS
};
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
// Load bait/mud assets

View File

@ -39,7 +39,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new WaveModulusRequirement([1, 2, 3], 10)) // Must be in first 3 waves after boss wave
.withSceneRequirement(new MoneyRequirement(undefined, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withIntroSpriteConfigs([
@ -73,7 +73,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(undefined, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
.withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,
@ -172,7 +172,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
// Defense/Spd buffs below wave 50, Atk/Def/Spd buffs otherwise
// Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];

View File

@ -34,7 +34,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_POKEMON_SALESMAN)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay
.withSceneRequirement(new MoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([
{
@ -59,11 +59,12 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!;
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
const tries = 0;
let tries = 0;
// Reroll any species that don't have HAs
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
tries++;
}
let pokemon: PlayerPokemon;
@ -71,7 +72,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
// If no HA mon found or you roll 1%, give shiny Magikarp
species = getPokemonSpecies(Species.MAGIKARP);
const hiddenIndex = species.ability2 ? 2 : 1;
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, undefined, true);
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex, undefined, true, 0);
} else {
const hiddenIndex = species.ability2 ? 2 : 1;
pokemon = new PlayerPokemon(scene, species, 5, hiddenIndex, species.formIndex);
@ -113,7 +114,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withHasDexProgress(true)
.withSceneMoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
.withSceneMoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2
.withDialogue({
buttonLabel: `${namespace}.option.1.label`,
buttonTooltip: `${namespace}.option.1.tooltip`,

View File

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

View File

@ -324,21 +324,18 @@ export const TrainingSessionEncounter: MysteryEncounter =
const abilityIndex = encounter.misc.abilityIndex;
if (!!playerPokemon.getFusionSpeciesForm()) {
playerPokemon.fusionAbilityIndex = abilityIndex;
if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies!.speciesId)) {
scene.gameData.starterData[playerPokemon.fusionSpecies!.speciesId]
if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) {
scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId]
.abilityAttr |=
abilityIndex !== 1 || playerPokemon.fusionSpecies!.ability2
abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2
? Math.pow(2, playerPokemon.fusionAbilityIndex)
: AbilityAttr.ABILITY_HIDDEN;
}
} else {
playerPokemon.abilityIndex = abilityIndex;
if (
speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)
) {
scene.gameData.starterData[
playerPokemon.species.speciesId
].abilityAttr |=
if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) {
scene.gameData.starterData[playerPokemon.species.speciesId]
.abilityAttr |=
abilityIndex !== 1 || playerPokemon.species.ability2
? Math.pow(2, playerPokemon.abilityIndex)
: AbilityAttr.ABILITY_HIDDEN;

View File

@ -16,7 +16,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
@ -24,6 +23,9 @@ const namespace = "mysteryEncounter:trashToTreasure";
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.
* @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- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
encounter.setDialogueToken("costMultiplier", SHOP_ITEM_COST_MULTIPLIER.toString());
return true;
})
.withOption(
@ -102,8 +106,14 @@ export const TrashToTreasureEncounter: MysteryEncounter =
transitionMysteryEncounterIntroVisuals(scene);
await tryApplyDigRewardItems(scene);
// Give the player the Black Sludge curse
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE));
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [SHOP_ITEM_COST_MULTIPLIER]);
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);
})
.build()
@ -180,7 +190,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) {
}
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + leftovers.name }), null, undefined, true);
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2x " + leftovers.name }), null, undefined, true);
// First Shell bell
for (const pokemon of party) {
@ -207,7 +217,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) {
}
scene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2 " + shellBell.name }), null, undefined, true);
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: "2x " + shellBell.name }), null, undefined, true);
}
async function doGarbageDig(scene: BattleScene) {

View File

@ -12,7 +12,6 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
import { TrainerSlot } from "#app/data/trainer-config";
import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data";
import { speciesEggMoves } from "#app/data/egg-moves";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle";
@ -53,11 +52,10 @@ export const UncommonBreedEncounter: MysteryEncounter =
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
const species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true);
const speciesRootForm = pokemon.species.getRootSpeciesId();
// Pokemon will always have one of its egg moves in its moveset
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
const eggMoves: Moves[] = speciesEggMoves[speciesRootForm];
const eggMoves = pokemon.getEggMoves();
if (eggMoves) {
const eggMoveIndex = randSeedInt(4);
const randomEggMove: Moves = eggMoves[eggMoveIndex];
encounter.misc = {
@ -72,6 +70,11 @@ export const UncommonBreedEncounter: MysteryEncounter =
encounter.misc.pokemon = pokemon;
// Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
const config: EnemyPartyConfig = {
pokemonConfigs: [{
level: level,
@ -81,7 +84,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}
}],
};
@ -193,20 +196,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
const pokemon = encounter.misc.pokemon;
// Give 1 additional egg move
const previousEggMove = encounter.misc.eggMove;
const speciesRootForm = pokemon.species.getRootSpeciesId();
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
const eggMoves: Moves[] = speciesEggMoves[speciesRootForm];
let randomEggMove: Moves = eggMoves[randSeedInt(4)];
while (randomEggMove === previousEggMove) {
randomEggMove = eggMoves[randSeedInt(4)];
}
if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
pokemon.moveset[1] = new PokemonMove(randomEggMove);
}
}
givePokemonExtraEggMove(pokemon, encounter.misc.eggMove);
await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false);
setEncounterRewards(scene, { fillRemaining: true });
@ -235,20 +225,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
const pokemon = encounter.misc.pokemon;
// Give 1 additional egg move
const previousEggMove = encounter.misc.eggMove;
const speciesRootForm = pokemon.species.getRootSpeciesId();
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
const eggMoves: Moves[] = speciesEggMoves[speciesRootForm];
let randomEggMove: Moves = eggMoves[randSeedInt(4)];
while (randomEggMove === previousEggMove) {
randomEggMove = eggMoves[randSeedInt(4)];
}
if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
pokemon.moveset[1] = new PokemonMove(randomEggMove);
}
}
givePokemonExtraEggMove(pokemon, encounter.misc.eggMove);
// Roll IVs a second time
pokemon.ivs = pokemon.ivs.map(iv => {
@ -266,3 +243,18 @@ export const UncommonBreedEncounter: MysteryEncounter =
.build()
)
.build();
function givePokemonExtraEggMove(pokemon: EnemyPokemon, previousEggMove: Moves) {
const eggMoves = pokemon.getEggMoves();
if (eggMoves) {
let randomEggMove: Moves = eggMoves[randSeedInt(4)];
while (randomEggMove === previousEggMove) {
randomEggMove = eggMoves[randSeedInt(4)];
}
if (pokemon.moveset.length < 4) {
pokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
pokemon.moveset[1] = new PokemonMove(randomEggMove);
}
}
}

View File

@ -12,7 +12,6 @@ import { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
import { achvs } from "#app/system/achv";
import { speciesEggMoves } from "#app/data/egg-moves";
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { modifierTypes } from "#app/modifier/modifier-type";
@ -105,7 +104,7 @@ const STANDARD_BST_TRANSFORM_BASE_VALUES: [number, number] = [40, 50];
export const WeirdDreamEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withDisallowedChallenges(Challenges.SINGLE_TYPE)
.withDisallowedChallenges(Challenges.SINGLE_TYPE, Challenges.SINGLE_GENERATION)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
@ -216,7 +215,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
pokemon.levelExp = 0;
pokemon.calculateStats();
pokemon.updateInfo();
await pokemon.updateInfo();
}
leaveEncounterWithoutBattle(scene, true);
@ -346,6 +345,9 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
// If the previous pokemon had pokerus, transfer to new pokemon
newPokemon.pokerus = previousPokemon.pokerus;
// Transfer previous Pokemon's luck value
newPokemon.luck = previousPokemon.getLuck();
// If the previous pokemon had higher IVs, override to those (after updating dex IVs > prevents perfect 31s on a new unlock)
newPokemon.ivs = newPokemon.ivs.map((iv, index) => {
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
@ -358,44 +360,15 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move and 1 (attempted) STAB move of the new species
newPokemon.generateAndPopulateMoveset();
// Try to find a favored STAB move
let favoredMove;
for (const move of newPokemon.moveset) {
// Needs to match first type, second type will be replaced
if (move?.getMove().type === newPokemon.getTypes()[0]) {
favoredMove = move;
break;
}
}
// If was unable to find a move, uses first move in moveset (typically a high power STAB move)
favoredMove = favoredMove ?? newPokemon.moveset[0];
// Store a copy of a "standard" generated moveset for the new pokemon, will be used later for finding a favored move
const newPokemonGeneratedMoveset = newPokemon.moveset;
newPokemon.moveset = previousPokemon.moveset;
let eggMoveIndex: null | number = null;
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
const eggMoves = speciesEggMoves[speciesRootForm];
const randomEggMoveIndex = randSeedInt(4);
const randomEggMove = eggMoves[randomEggMoveIndex];
if (newPokemon.moveset.length < 4) {
newPokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
eggMoveIndex = randSeedInt(4);
newPokemon.moveset[eggMoveIndex] = new PokemonMove(randomEggMove);
}
// For pokemon that the player owns (including ones just caught), unlock the egg move
if (!!scene.gameData.dexData[speciesRootForm].caughtAttr) {
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
}
}
if (favoredMove) {
let favoredMoveIndex = randSeedInt(4);
while (favoredMoveIndex === eggMoveIndex) {
favoredMoveIndex = randSeedInt(4);
}
newPokemon.moveset[favoredMoveIndex] = favoredMove;
}
const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm);
// Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset)
addFavoredMoveToNewPokemonMoveset(scene, newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
// Randomize the second type of the pokemon
// If the pokemon does not normally have a second type, it will gain 1
@ -412,7 +385,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
for (const item of transformation.heldItems) {
item.pokemonId = newPokemon.id;
scene.addModifier(item, false, false, false, true);
await scene.addModifier(item, false, false, false, true);
}
// Any pokemon that is at or below 450 BST gets +20 permanent BST to 3 stats: HP (halved, +10), lowest of Atk/SpAtk, and lowest of Def/SpDef
@ -423,11 +396,12 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
stats.push(baseStats[Stat.ATK] < baseStats[Stat.SPATK] ? Stat.ATK : Stat.SPATK);
// Def or SpDef
stats.push(baseStats[Stat.DEF] < baseStats[Stat.SPDEF] ? Stat.DEF : Stat.SPDEF);
// const mod = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().newModifier(newPokemon, 20, stats);
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().generateType(scene.getParty(), [20, stats]);
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
.generateType(scene.getParty(), [20, stats])
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
const modifier = modType?.newModifier(newPokemon);
if (modifier) {
scene.addModifier(modifier);
await scene.addModifier(modifier, false, false, false, true);
}
}
@ -435,13 +409,15 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
newPokemon.passive = previousPokemon.passive;
newPokemon.calculateStats();
newPokemon.initBattleInfo();
await newPokemon.updateInfo();
}
// One random pokemon will get its passive unlocked
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
if (passiveDisabledPokemon?.length > 0) {
passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)].passive = true;
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
enablePassiveMon.passive = true;
await enablePassiveMon.updateInfo(true);
}
// If at least one new starter was unlocked, play 1 fanfare
@ -471,7 +447,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num
if (validSpecies?.length > 20) {
validSpecies = randSeedShuffle(validSpecies);
newSpecies = validSpecies.pop();
while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) {
while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies)) {
newSpecies = validSpecies.pop();
}
} else {
@ -481,7 +457,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num
}
}
return newSpecies!;
return newSpecies;
}
function doShowDreamBackground(scene: BattleScene) {
@ -566,3 +542,83 @@ function doSideBySideTransformations(scene: BattleScene, transformations: Pokemo
}
});
}
/**
* Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`)
* @param scene
* @param newPokemon
* @param speciesRootForm
*/
async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, speciesRootForm: Species): Promise<number | null> {
let eggMoveIndex: null | number = null;
const eggMoves = newPokemon.getEggMoves()?.slice(0);
if (eggMoves) {
const eggMoveIndices = [0, 1, 2, 3];
randSeedShuffle(eggMoveIndices);
let randomEggMoveIndex = eggMoveIndices.pop();
let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
let retries = 0;
while (retries < 3 && (!randomEggMove || newPokemon.moveset.some(m => m?.moveId === randomEggMove))) {
// If Pokemon already knows this move, roll for another egg move
randomEggMoveIndex = eggMoveIndices.pop();
randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
retries++;
}
if (randomEggMove) {
if (!newPokemon.moveset.some(m => m?.moveId === randomEggMove)) {
if (newPokemon.moveset.length < 4) {
newPokemon.moveset.push(new PokemonMove(randomEggMove));
} else {
eggMoveIndex = randSeedInt(4);
newPokemon.moveset[eggMoveIndex] = new PokemonMove(randomEggMove);
}
}
// For pokemon that the player owns (including ones just caught), unlock the egg move
if (!isNullOrUndefined(randomEggMoveIndex) && !!scene.gameData.dexData[speciesRootForm].caughtAttr) {
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
}
}
}
return eggMoveIndex;
}
/**
* Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`)
* @param scene
* @param newPokemon
* @param newPokemonGeneratedMoveset
* @param newEggMoveIndex
*/
function addFavoredMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, newPokemonGeneratedMoveset: (PokemonMove | null)[], newEggMoveIndex: number | null) {
let favoredMove: PokemonMove | null = null;
for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced
if (move?.getMove().type === newPokemon.getTypes()[0] && !newPokemon.moveset.some(m => m?.moveId === move?.moveId)) {
favoredMove = move;
break;
}
}
// If was unable to find a favored move, uses first move in moveset that isn't already known (typically a high power STAB move)
// Otherwise, it gains no favored move
if (!favoredMove) {
for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced
if (!newPokemon.moveset.some(m => m?.moveId === move?.moveId)) {
favoredMove = move;
break;
}
}
}
// Finally, assign favored move to random index that isn't the new egg move index
if (favoredMove) {
let favoredMoveIndex = randSeedInt(4);
while (newEggMoveIndex !== null && favoredMoveIndex === newEggMoveIndex) {
favoredMoveIndex = randSeedInt(4);
}
newPokemon.moveset[favoredMoveIndex] = favoredMove;
}
}

View File

@ -208,7 +208,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
return Object.assign(this, { requirements: this.requirements });
}
withSceneMoneyRequirement(requiredMoney?: number, scalingMultiplier?: number) {
withSceneMoneyRequirement(requiredMoney: number, scalingMultiplier?: number) {
return this.withSceneRequirement(new MoneyRequirement(requiredMoney, scalingMultiplier));
}

View File

@ -165,9 +165,9 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
}
override meetsRequirement(scene: BattleScene): boolean {
if (!isNullOrUndefined(this.waveRange) && this.waveRange?.[0] <= this.waveRange?.[1]) {
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
const waveIndex = scene.currentBattle.waveIndex;
if (waveIndex >= 0 && (this.waveRange?.[0] >= 0 && this.waveRange?.[0] > waveIndex) || (this.waveRange?.[1] >= 0 && this.waveRange?.[1] < waveIndex)) {
if (waveIndex >= 0 && (this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)) {
return false;
}
}
@ -251,7 +251,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
const currentWeather = scene.arena.weather?.weatherType;
let token = "";
if (!isNullOrUndefined(currentWeather)) {
token = WeatherType[currentWeather!].replace("_", " ").toLocaleLowerCase();
token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase();
}
return ["weather", token];
}
@ -274,9 +274,9 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
}
override meetsRequirement(scene: BattleScene): boolean {
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange?.[0] <= this.partySizeRange?.[1]) {
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) {
const partySize = this.excludeDisallowedPokemon ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length;
if (partySize >= 0 && (this.partySizeRange?.[0] >= 0 && this.partySizeRange?.[0] > partySize) || (this.partySizeRange?.[1] >= 0 && this.partySizeRange?.[1] < partySize)) {
if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) {
return false;
}
}
@ -326,7 +326,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
requiredMoney: number; // Static value
scalingMultiplier: number; // Calculates required money based off wave index
constructor(requiredMoney?: number, scalingMultiplier?: number) {
constructor(requiredMoney: number, scalingMultiplier?: number) {
super();
this.requiredMoney = requiredMoney ?? 0;
this.scalingMultiplier = scalingMultiplier ?? 0;
@ -418,8 +418,8 @@ export class NatureRequirement extends EncounterPokemonRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon!.nature)) {
return ["nature", Nature[pokemon!.nature]];
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) {
return ["nature", Nature[pokemon.nature]];
}
return ["nature", ""];
}
@ -620,7 +620,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
return this.requiredStatusEffect.some((statusEffect) => {
if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect;
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === statusEffect;
} else {
return pokemon.status?.effect === statusEffect;
}
@ -628,12 +628,11 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
});
} else {
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed StatusEffects
// return partyPokemon.filter((pokemon) => this.requiredStatusEffect.filter((statusEffect) => pokemon.status?.effect === statusEffect).length === 0);
return partyPokemon.filter((pokemon) => {
return !this.requiredStatusEffect.some((statusEffect) => {
if (statusEffect === StatusEffect.NONE) {
// StatusEffect.NONE also checks for null or undefined status
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect;
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === statusEffect;
} else {
return pokemon.status?.effect === statusEffect;
}
@ -645,7 +644,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const reqStatus = this.requiredStatusEffect.filter((a) => {
if (a === StatusEffect.NONE) {
return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon!.status!.effect) || pokemon!.status!.effect === a;
return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === a;
}
return pokemon!.status?.effect === a;
});
@ -988,8 +987,9 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
}
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.getHpRatio())) {
return ["healthRatio", Math.floor(pokemon!.getHpRatio() * 100).toString() + "%"];
const hpRatio = pokemon?.getHpRatio();
if (!isNullOrUndefined(hpRatio)) {
return ["healthRatio", Math.floor(hpRatio * 100).toString() + "%"];
}
return ["healthRatio", ""];
}

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 { 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 { 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
@ -362,11 +363,16 @@ export function initMysteryEncounters() {
});
// Add ANY biome encounters to biome map
mysteryEncountersByBiome.forEach(biomeEncounters => {
let encounterBiomeTableLog = "";
mysteryEncountersByBiome.forEach((biomeEncounters, biome) => {
anyBiomeEncounters.forEach(encounter => {
if (!biomeEncounters.includes(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

@ -17,7 +17,7 @@ export function getEncounterText(scene: BattleScene, keyOrString?: string, prima
return null;
}
let textString: string | null = getTextWithDialogueTokens(scene, keyOrString!);
let textString: string | null = getTextWithDialogueTokens(scene, keyOrString);
// Can only color the text if a Primary Style is defined
// primaryStyle is applied to all text that does not have its own specified style

View File

@ -135,7 +135,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
scene.currentBattle.trainer.destroy();
}
trainerConfig = partyConfig?.trainerConfig ? partyConfig?.trainerConfig : trainerConfigs[trainerType!];
trainerConfig = partyTrainerConfig ? partyTrainerConfig : trainerConfigs[trainerType!];
const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle);
doubleBattle = doubleTrainer;
@ -166,7 +166,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// This can be amplified or counteracted by setting levelAdditiveModifier in config
// levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc.
// Leaving null/undefined will disable level scaling
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier! : 0;
const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0;
const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0);
battle.enemyLevels = battle.enemyLevels.map(level => level + additive);
@ -226,7 +226,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// Set form
if (!isNullOrUndefined(config.nickname)) {
enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname!)));
enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname)));
}
// Generate new id, reset status and HP in case using data source
@ -236,29 +236,29 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// Set form
if (!isNullOrUndefined(config.formIndex)) {
enemyPokemon.formIndex = config.formIndex!;
enemyPokemon.formIndex = config.formIndex;
}
// Set shiny
if (!isNullOrUndefined(config.shiny)) {
enemyPokemon.shiny = config.shiny!;
enemyPokemon.shiny = config.shiny;
}
// Set Variant
if (enemyPokemon.shiny && !isNullOrUndefined(config.variant)) {
enemyPokemon.variant = config.variant!;
enemyPokemon.variant = config.variant;
}
// Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.)
if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) {
enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!;
enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData;
}
// Set Boss
if (config.isBoss) {
let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments! : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true);
if (!isNullOrUndefined(config.bossSegmentModifier)) {
segments += config.bossSegmentModifier!;
segments += config.bossSegmentModifier;
}
enemyPokemon.setBoss(true, segments);
}
@ -294,18 +294,18 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
// Set ability
if (!isNullOrUndefined(config.abilityIndex)) {
enemyPokemon.abilityIndex = config.abilityIndex!;
enemyPokemon.abilityIndex = config.abilityIndex;
}
// Set gender
if (!isNullOrUndefined(config.gender)) {
enemyPokemon.gender = config.gender!;
enemyPokemon.summonData.gender = config.gender!;
enemyPokemon.summonData.gender = config.gender;
}
// Set AI type
if (!isNullOrUndefined(config.aiType)) {
enemyPokemon.aiType = config.aiType!;
enemyPokemon.aiType = config.aiType;
}
// Set moves
@ -339,7 +339,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
loadEnemyAssets.push(enemyPokemon.loadAssets());
console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
console.log(`Pokemon: ${enemyPokemon.name}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`);
});
scene.pushPhase(new MysteryEncounterBattlePhase(scene, partyConfig.disableSwitch));

View File

@ -218,7 +218,7 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu
.map(s => [getPokemonSpecies(s[0]), s[1]]);
if (types && types.length > 0) {
filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || (!isNullOrUndefined(s[0].type2) && types.includes(s[0].type2!)));
filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || (!isNullOrUndefined(s[0].type2) && types.includes(s[0].type2)));
}
// If no filtered mons exist at specified starter tiers, will expand starter search range until there are
@ -311,7 +311,9 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
* @param value
*/
export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE().generateType(pokemon.scene.getParty(), [value]);
const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE()
.generateType(pokemon.scene.getParty(), [value])
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE);
const modifier = modType?.newModifier(pokemon);
if (modifier) {
await pokemon.scene.addModifier(modifier, false, false, false, true);
@ -780,8 +782,7 @@ export function getGoldenBugNetSpecies(): PokemonSpecies {
*/
export function getEncounterPokemonLevelForWave(scene: BattleScene, levelAdditiveModifier: number = 0) {
const currentBattle = scene.currentBattle;
// Default to use the first generated level from enemyLevels, or generate a new one if it DNE
const baseLevel = currentBattle.enemyLevels && currentBattle.enemyLevels?.length > 0 ? currentBattle.enemyLevels[0] : currentBattle.getLevelForWave();
const baseLevel = currentBattle.getLevelForWave();
// Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier
return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0);

View File

@ -44,6 +44,10 @@ function getStatusEffectMessageKey(statusEffect: StatusEffect | undefined): stri
}
export function getStatusEffectObtainText(statusEffect: StatusEffect | undefined, pokemonNameWithAffix: string, sourceText?: string): string {
if (statusEffect === StatusEffect.NONE) {
return "";
}
if (!sourceText) {
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain`as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
@ -53,21 +57,33 @@ export function getStatusEffectObtainText(statusEffect: StatusEffect | undefined
}
export function getStatusEffectActivationText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
if (statusEffect === StatusEffect.NONE) {
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.activation` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
}
export function getStatusEffectOverlapText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
if (statusEffect === StatusEffect.NONE) {
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.overlap` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
}
export function getStatusEffectHealText(statusEffect: StatusEffect, pokemonNameWithAffix: string): string {
if (statusEffect === StatusEffect.NONE) {
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.heal` as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });
}
export function getStatusEffectDescriptor(statusEffect: StatusEffect): string {
if (statusEffect === StatusEffect.NONE) {
return "";
}
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.description` as ParseKeys;
return i18next.t(i18nKey);
}

View File

@ -597,7 +597,7 @@ export class TrainerConfig {
case "flare": {
return {
[TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY],
[TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.BERGMITE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO],
[TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO],
[TrainerPoolTier.RARE]: [Species.NOIVERN, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO]
};
}
@ -640,14 +640,14 @@ export class TrainerConfig {
return {
[TrainerPoolTier.COMMON]: [ Species.ZUBAT, Species.GRIMER, Species.STUNKY, Species.FOONGUS, Species.MAREANIE, Species.TOXEL, Species.SHROODLE, Species.PALDEA_WOOPER ],
[TrainerPoolTier.UNCOMMON]: [ Species.GASTLY, Species.SEVIPER, Species.SKRELP, Species.ALOLA_GRIMER, Species.GALAR_SLOWPOKE, Species.HISUI_QWILFISH ],
[TrainerPoolTier.RARE]: [ Species.BULBASAUR, Species.GLIMMET ]
[TrainerPoolTier.RARE]: [ Species.GLIMMET, Species.BULBASAUR ]
};
}
case "star_4": {
return {
[TrainerPoolTier.COMMON]: [ Species.CLEFFA, Species.IGGLYBUFF, Species.AZURILL, Species.COTTONEE, Species.FLABEBE, Species.HATENNA, Species.IMPIDIMP, Species.TINKATINK ],
[TrainerPoolTier.UNCOMMON]: [ Species.TOGEPI, Species.GARDEVOIR, Species.SYLVEON, Species.KLEFKI, Species.MIMIKYU, Species.ALOLA_VULPIX ],
[TrainerPoolTier.RARE]: [ Species.POPPLIO, Species.GALAR_PONYTA ]
[TrainerPoolTier.RARE]: [ Species.GALAR_PONYTA, Species.POPPLIO ]
};
}
case "star_5": {
@ -1509,7 +1509,7 @@ export const trainerConfigs: TrainerConfigs = {
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH],
[TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA],
[TrainerPoolTier.RARE]: [Species.MANTINE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP],
[TrainerPoolTier.RARE]: [Species.MANTYKE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP],
[TrainerPoolTier.SUPER_RARE]: [Species.DONDOZO, Species.BASCULEGION]
}),
[TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
@ -1527,8 +1527,8 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE],
[TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK],
[TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK, Species.CUBCHOO, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT],
[TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK, Species.CUBCHOO, Species.KLINK],
[TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT],
[TrainerPoolTier.SUPER_RARE]: [Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO]
}),
[TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.CRYOGONAL]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
@ -1537,7 +1537,7 @@ export const trainerConfigs: TrainerConfigs = {
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR],
[TrainerPoolTier.UNCOMMON]: [Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP],
[TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.BERGMITE, Species.SLIGGOO],
[TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO],
[TrainerPoolTier.SUPER_RARE]: [Species.NOIVERN, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG]
}),
[TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [Species.LIEPARD]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
@ -1545,15 +1545,15 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU],
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK],
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.TURTONATOR, Species.DRAMPA],
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK],
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.TURTONATOR, Species.MIMIKYU, Species.MAGNEMITE, Species.DRAMPA],
[TrainerPoolTier.SUPER_RARE]: [Species.JANGMO_O, Species.PORYGON]
}),
[TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [Species.HYPNO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
[TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH],
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.DROWZEE, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY],
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH, Species.DROWZEE],
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY],
[TrainerPoolTier.RARE]: [Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE],
[TrainerPoolTier.SUPER_RARE]: [Species.GRUBBIN, Species.DEWPIDER]
}),
@ -1916,7 +1916,14 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1; // Mega Kangaskhan
p.generateName();
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON, Species.SEISMITOAD]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON, Species.SEISMITOAD], TrainerSlot.TRAINER, true, p => {
//Storm Drain Gastrodon, Water Absorb Seismitoad
if (p.species.speciesId === Species.GASTRODON) {
p.abilityIndex = 0;
} else if (p.species.speciesId === Species.SEISMITOAD) {
p.abilityIndex = 2;
}
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.MEWTWO], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
@ -2060,7 +2067,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.formIndex = Utils.randSeedInt(5, 1); // Shock, Burn, Chill, or Douse Drive
p.formIndex = Utils.randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BASCULEGION, Species.JELLICENT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2153,9 +2160,23 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.MASTER_BALL;
})),
[TrainerType.GUZMA]: new TrainerConfig(++t).setName("Guzma").initForEvilTeamLeader("Skull Boss", []).setMixedBattleBgm("battle_skull_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LOKIX, Species.YANMEGA ]))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LOKIX, Species.YANMEGA ], TrainerSlot.TRAINER, true, p => {
//Tinted Lens Lokix, Tinted Lens Yanmega
if (p.species.speciesId === Species.LOKIX) {
p.abilityIndex = 2;
} else if (p.species.speciesId === Species.YANMEGA) {
p.abilityIndex = 1;
}
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HERACROSS ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ], TrainerSlot.TRAINER, true, p => {
//Technician Scizor, Sharpness Kleavor
if (p.species.speciesId === Species.SCIZOR) {
p.abilityIndex = 1;
} else if (p.species.speciesId === Species.KLEAVOR) {
p.abilityIndex = 2;
}
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALVANTULA, Species.VIKAVOLT]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
@ -2175,25 +2196,32 @@ export const trainerConfigs: TrainerConfigs = {
p.abilityIndex = 2; //Anticipation
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_SAMUROTT, Species.CRAWDAUNT ], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ], TrainerSlot.TRAINER, true, p => {
//Technician Scizor, Sharpness Kleavor
if (p.species.speciesId === Species.SCIZOR) {
p.abilityIndex = 1;
} else if (p.species.speciesId === Species.KLEAVOR) {
p.abilityIndex = 2;
}
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.HISUI_SAMUROTT, Species.CRAWDAUNT ], TrainerSlot.TRAINER, true, p => {
p.abilityIndex = 2; //Sharpness Hisui Samurott, Adaptability Crawdaunt
}))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.BUZZWOLE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.formIndex = 1;
p.generateAndPopulateMoveset();
p.generateName();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.BUZZWOLE ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
}))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ROGUE_BALL;
})),
[TrainerType.ROSE]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", []).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ]))
@ -2209,17 +2237,16 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL;
})),
[TrainerType.ROSE_2]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", [], true).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.MELMETAL ], TrainerSlot.TRAINER, true, p => {
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AEGISLASH, Species.GHOLDENGO ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.DRACOVISH, Species.DRACOZOLT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
p.abilityIndex = 1; //Strong Jaw Dracovish, Hustle Dracozolt
}))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.ARCHALUDON ]))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MELMETAL ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GALAR_ARTICUNO, Species.GALAR_ZAPDOS, Species.GALAR_MOLTRES ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();

View File

@ -79,6 +79,7 @@ export enum BattlerTagType {
TAR_SHOT = "TAR_SHOT",
BURNED_UP = "BURNED_UP",
DOUBLE_SHOCKED = "DOUBLE_SHOCKED",
AUTOTOMIZED = "AUTOTOMIZED",
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON",
HEAL_BLOCK = "HEAL_BLOCK",
}

View File

@ -33,6 +33,7 @@ export class Arena {
public tags: ArenaTag[];
public bgm: string;
public ignoreAbilities: boolean;
public ignoringEffectSource: BattlerIndex | null;
private lastTimeOfDay: TimeOfDay;
@ -569,8 +570,9 @@ export class Arena {
}
}
setIgnoreAbilities(ignoreAbilities: boolean = true): void {
setIgnoreAbilities(ignoreAbilities: boolean, ignoringEffectSource: BattlerIndex | null = null): void {
this.ignoreAbilities = ignoreAbilities;
this.ignoringEffectSource = ignoreAbilities ? ignoringEffectSource : null;
}
/**

View File

@ -86,7 +86,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
};
if (!isNullOrUndefined(result.species)) {
const keys = getSpriteKeysFromSpecies(result.species!);
const keys = getSpriteKeysFromSpecies(result.species);
result.spriteKey = keys.spriteKey;
result.fileRoot = keys.fileRoot;
result.isPokemon = true;

View File

@ -17,10 +17,10 @@ import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
import { Status, StatusEffect, getRandomStatus } from "../data/status-effect";
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions";
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms";
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag } from "../data/battler-tags";
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag } from "../data/battler-tags";
import { WeatherType } from "../data/weather";
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "../data/ability";
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs } from "../data/ability";
import PokemonData from "../system/pokemon-data";
import { BattlerIndex } from "../battle";
import { Mode } from "../ui/ui";
@ -1258,8 +1258,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
}
if (this.isFusion()) {
if (!isNullOrUndefined(this.fusionMysteryEncounterPokemonData?.ability) && this.fusionMysteryEncounterPokemonData!.ability !== -1) {
return allAbilities[this.fusionMysteryEncounterPokemonData!.ability];
if (!isNullOrUndefined(this.fusionMysteryEncounterPokemonData?.ability) && this.fusionMysteryEncounterPokemonData.ability !== -1) {
return allAbilities[this.fusionMysteryEncounterPokemonData.ability];
} else {
return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)];
}
@ -1364,7 +1364,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (this.isFusion() && ability.hasAttr(NoFusionAbilityAbAttr)) {
return false;
}
if (this.scene?.arena.ignoreAbilities && ability.isIgnorable) {
const arena = this.scene?.arena;
if (arena.ignoreAbilities && arena.ignoringEffectSource !== this.getBattlerIndex() && ability.isIgnorable) {
return false;
}
if (this.summonData?.abilitySuppressed && !ability.hasAttr(UnsuppressableAbilityAbAttr)) {
@ -1426,11 +1427,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
/**
* Gets the weight of the Pokemon with subtractive modifiers (Autotomize) happening first
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
* @returns the kg of the Pokemon (minimum of 0.1)
*/
getWeight(): number {
const weight = new Utils.NumberHolder(this.species.weight);
const autotomizedTag = this.getTag(AutotomizedTag);
let weightRemoved = 0;
if (!Utils.isNullOrUndefined(autotomizedTag)) {
weightRemoved = 100 * autotomizedTag!.autotomizeCount;
}
const minWeight = 0.1;
const weight = new Utils.NumberHolder(this.species.weight - weightRemoved);
// This will trigger the ability overlay so only call this function when necessary
applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight);
return weight.value;
return Math.max(minWeight, weight.value);
}
/**
@ -1786,8 +1799,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*
* @returns list of egg moves
*/
getEggMoves() : Moves[] {
return speciesEggMoves[this.species.speciesId];
getEggMoves() : Moves[] | undefined {
return speciesEggMoves[this.getSpeciesForm().getRootSpeciesId(true)];
}
setMove(moveIndex: integer, moveId: Moves): void {
@ -3365,7 +3378,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
if (asPhase) {
this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText!, sourcePokemon!)); // TODO: are these bangs correct?
this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText, sourcePokemon));
return true;
}
@ -3399,6 +3412,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (effect !== StatusEffect.FAINT) {
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeStatusEffectTrigger, true);
applyPostSetStatusAbAttrs(PostSetStatusAbAttr, this, effect, sourcePokemon);
}
return true;

View File

@ -1,5 +1,4 @@
import { GachaType } from "./enums/gacha-types";
import { trainerConfigs } from "./data/trainer-config";
import { getBiomeHasProps } from "./field/arena";
import CacheBustedLoaderPlugin from "./plugins/cache-busted-loader-plugin";
import { SceneBase } from "./scene-base";
@ -21,7 +20,6 @@ import i18next from "i18next";
import { initStatsKeys } from "./ui/game-stats-ui-handler";
import { initVouchers } from "./system/voucher";
import { Biome } from "#enums/biome";
import { TrainerType } from "#enums/trainer-type";
import {initMysteryEncounters} from "#app/data/mystery-encounters/mystery-encounters";
export class LoadingScene extends SceneBase {
@ -208,14 +206,6 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("trainer_f_back", "trainer");
this.loadAtlas("trainer_f_back_pb", "trainer");
Utils.getEnumValues(TrainerType).map(tt => {
const config = trainerConfigs[tt];
this.loadAtlas(config.getSpriteKey(), "trainer");
if (config.doubleOnly || config.hasDouble) {
this.loadAtlas(config.getSpriteKey(true), "trainer");
}
});
// Load character sprites
this.loadAtlas("c_rival_m", "character", "rival_m");
this.loadAtlas("c_rival_f", "character", "rival_f");

View File

@ -53,7 +53,49 @@ import terrain from "./terrain.json";
import modifierSelectUiHandler from "./modifier-select-ui-handler.json";
import moveTriggers from "./move-trigger.json";
import runHistory from "./run-history.json";
import mysteryEncounterMessages from "./mystery-encounter-messages.json";
import lostAtSea from "./mystery-encounters/lost-at-sea-dialogue.json";
import mysteriousChest from "./mystery-encounters/mysterious-chest-dialogue.json";
import mysteriousChallengers from "./mystery-encounters/mysterious-challengers-dialogue.json";
import darkDeal from "./mystery-encounters/dark-deal-dialogue.json";
import departmentStoreSale from "./mystery-encounters/department-store-sale-dialogue.json";
import fieldTrip from "./mystery-encounters/field-trip-dialogue.json";
import fieryFallout from "./mystery-encounters/fiery-fallout-dialogue.json";
import fightOrFlight from "./mystery-encounters/fight-or-flight-dialogue.json";
import safariZone from "./mystery-encounters/safari-zone-dialogue.json";
import shadyVitaminDealer from "./mystery-encounters/shady-vitamin-dealer-dialogue.json";
import slumberingSnorlax from "./mystery-encounters/slumbering-snorlax-dialogue.json";
import trainingSession from "./mystery-encounters/training-session-dialogue.json";
import theStrongStuff from "./mystery-encounters/the-strong-stuff-dialogue.json";
import pokemonSalesman from "./mystery-encounters/the-pokemon-salesman-dialogue.json";
import offerYouCantRefuse from "./mystery-encounters/an-offer-you-cant-refuse-dialogue.json";
import delibirdy from "./mystery-encounters/delibirdy-dialogue.json";
import absoluteAvarice from "./mystery-encounters/absolute-avarice-dialogue.json";
import aTrainersTest from "./mystery-encounters/a-trainers-test-dialogue.json";
import trashToTreasure from "./mystery-encounters/trash-to-treasure-dialogue.json";
import berriesAbound from "./mystery-encounters/berries-abound-dialogue.json";
import clowningAround from "./mystery-encounters/clowning-around-dialogue.json";
import partTimer from "./mystery-encounters/part-timer-dialogue.json";
import dancingLessons from "./mystery-encounters/dancing-lessons-dialogue.json";
import weirdDream from "./mystery-encounters/weird-dream-dialogue.json";
import theWinstrateChallenge from "./mystery-encounters/the-winstrate-challenge-dialogue.json";
import teleportingHijinks from "./mystery-encounters/teleporting-hijinks-dialogue.json";
import bugTypeSuperfan from "./mystery-encounters/bug-type-superfan-dialogue.json";
import funAndGames from "./mystery-encounters/fun-and-games-dialogue.json";
import uncommonBreed from "./mystery-encounters/uncommon-breed-dialogue.json";
import globalTradeSystem from "./mystery-encounters/global-trade-system-dialogue.json";
import expertPokemonBreeder from "./mystery-encounters/the-expert-pokemon-breeder-dialogue.json";
/**
* Dialogue/Text token injection patterns that can be used:
* - `$` will be treated as a new line for Message and Dialogue strings.
* - `@d{<number>}` will add a time delay to text animation for Message and Dialogue strings.
* - `@s{<sound_effect_key>}` will play a specified sound effect for Message and Dialogue strings.
* - `@f{<number>}` will fade the screen to black for the given duration, then fade back in for Message and Dialogue strings.
* - `{{<token>}}` (MYSTERY ENCOUNTERS ONLY) will auto-inject the matching dialogue token value that is stored in {@link IMysteryEncounter.dialogueTokens}.
* - (see [i18next interpolations](https://www.i18next.com/translation-function/interpolation)) for more details.
* - `@[<TextStyle>]{<text>}` (STATIC TEXT ONLY, NOT USEABLE WITH {@link UI.showText()} OR {@link UI.showDialogue()}) will auto-color the given text to a specified {@link TextStyle} (e.g. `TextStyle.SUMMARY_GREEN`).
*/
export const caEsConfig = {
ability,
abilityTriggers,
@ -110,4 +152,40 @@ export const caEsConfig = {
modifierSelectUiHandler,
moveTriggers,
runHistory,
mysteryEncounter: {
// DO NOT REMOVE
"unit_test_dialogue": "{{test}}{{test}} {{test{{test}}}} {{test1}} {{test\}} {{test\\}} {{test\\\}} {test}}",
mysteriousChallengers,
mysteriousChest,
darkDeal,
fightOrFlight,
slumberingSnorlax,
trainingSession,
departmentStoreSale,
shadyVitaminDealer,
fieldTrip,
safariZone,
lostAtSea,
fieryFallout,
theStrongStuff,
pokemonSalesman,
offerYouCantRefuse,
delibirdy,
absoluteAvarice,
aTrainersTest,
trashToTreasure,
berriesAbound,
clowningAround,
partTimer,
dancingLessons,
weirdDream,
theWinstrateChallenge,
teleportingHijinks,
bugTypeSuperfan,
funAndGames,
uncommonBreed,
globalTradeSystem,
expertPokemonBreeder
},
mysteryEncounterMessages
};

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -1237,6 +1237,6 @@
},
"poisonPuppeteer": {
"name": "Giftpuppenspiel",
"description": "Wenn Infamomo ein Ziel mit einer Attacke vergiftet, so wird dieses auch verwirrt."
"description": "Wenn das Pokémon ein Ziel mit einer Attacke vergiftet, so wird dieses auch verwirrt."
}
}

View File

@ -273,5 +273,9 @@
"INVERSE_BATTLE": {
"name": "Spieglein, Spieglein an der Wand",
"description": "Schließe die 'Umkehrkampf' Herausforderung ab"
},
"BREEDERS_IN_SPACE": {
"name": "Züchter im Weltall!",
"description": "Besiege die Pokémon-Züchter-Expertin im Stratosphären Biome."
}
}

View File

@ -14,6 +14,10 @@
"moneyWon": "Du gewinnst {{moneyAmount}} ₽!",
"moneyPickedUp": "Du hebst {{moneyAmount}} ₽ auf!",
"pokemonCaught": "{{pokemonName}} wurde gefangen!",
"pokemonObtained": "Du erhältst {{pokemonName}}!",
"pokemonBrokeFree": "Mist!\nDas Pokémon hat sich befreit!",
"pokemonFled": "Das wilde {{pokemonName}} ist geflohen!",
"playerFled": "Du bist vor dem wilden {{pokemonName}} geflohen!",
"addedAsAStarter": "{{pokemonName}} wurde als Starterpokémon hinzugefügt!",
"partyFull": "Dein Team ist voll. Möchtest du ein Pokémon durch {{pokemonName}} ersetzen?",
"pokemon": "Pokémon",
@ -49,6 +53,7 @@
"noPokeballTrainer": "Du kannst das Pokémon eines anderen Trainers nicht fangen!",
"noPokeballMulti": "Du kannst erst einen Pokéball werfen, wenn nur noch ein Pokémon übrig ist!",
"noPokeballStrong": "Das Ziel-Pokémon ist zu stark, um gefangen zu werden! Du musst es zuerst schwächen!",
"noPokeballMysteryEncounter": "You aren't able to\ncatch this Pokémon!",
"noEscapeForce": "Eine unsichtbare Kraft verhindert die Flucht.",
"noEscapeTrainer": "Du kannst nicht aus einem Trainerkampf fliehen!",
"noEscapePokemon": "{{moveName}} von {{pokemonName}} verhindert {{escapeVerb}}!",
@ -97,6 +102,7 @@
"congratulations": "Glückwunsch!",
"beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!",
"eggSkipPrompt": "Zur Ei-Zusammenfassung springen?",
"mysteryEncounterAppeared": "Was ist das?",
"battlerTagsHealBlock": "{{pokemonNameWithAffix}} kann nicht geheilt werden, da die Heilung blockiert wird!",
"battlerTagsHealBlockOnRemove": "{{pokemonNameWithAffix}} kann wieder geheilt werden!"
}

View File

@ -77,20 +77,20 @@
"end_summit": "PMDDX Gipfel des Himmelturms",
"battle_rocket_grunt": "HGSS Vs. Team Rocket Rüpel",
"battle_aqua_magma_grunt": "ORAS Vs. Team Aqua & Magma",
"battle_galactic_grunt": "BDSP Vs. Team Galaktik Rüpel",
"battle_galactic_grunt": "SDLP Vs. Team Galaktik Rüpel",
"battle_plasma_grunt": "SW Vs. Team Plasma Rüpel",
"battle_flare_grunt": "XY Vs. Team Flare Rüpel",
"battle_aether_grunt": "SM Vs. Æther Foundation",
"battle_skull_grunt": "SM Vs. Team Skull Rüpel",
"battle_macro_grunt": "SWSH Vs. Trainer",
"battle_star_grunt": "KAPU Vs. Team Star",
"battle_galactic_admin": "BDSP Vs. Team Galactic Commander",
"battle_galactic_admin": "SDLP Vs. Team Galactic Commander",
"battle_skull_admin": "SM Vs. Team Skull Vorstand",
"battle_oleana": "SWSH Vs. Olivia",
"battle_star_admin": "KAPU Vs. Team Star Boss",
"battle_rocket_boss": "USUM Vs. Giovanni",
"battle_aqua_magma_boss": "ORAS Vs. Team Aqua & Magma Boss",
"battle_galactic_boss": "BDSP Vs. Zyrus",
"battle_galactic_boss": "SDLP Vs. Zyrus",
"battle_plasma_boss": "S2W2 Vs. G-Cis",
"battle_flare_boss": "XY Vs. Flordelis",
"battle_aether_boss": "SM Vs. Samantha",
@ -111,17 +111,17 @@
"forest": "PMD Erkundungsteam Himmel Düsterwald",
"grass": "PMD Erkundungsteam Himmel Apfelwald",
"graveyard": "PMD Erkundungsteam Himmel Verwirrwald",
"ice_cave": "PMD Erkundungsteam Himmel Rieseneisberg",
"ice_cave": "Firel - -50°C",
"island": "PMD Erkundungsteam Himmel Schroffküste",
"jungle": "Lmz - Jungle",
"laboratory": "Firel - Laboratory",
"lake": "PMD Erkundungsteam Himmel Kristallhöhle",
"lake": "Lmz - Lake",
"meadow": "PMD Erkundungsteam Himmel Himmelsgipfel-Wald",
"metropolis": "Firel - Metropolis",
"mountain": "PMD Erkundungsteam Himmel Hornberg",
"plains": "PMD Erkundungsteam Himmel Himmelsgipfel-Prärie",
"power_plant": "PMD Erkundungsteam Himmel Weite Ampere-Ebene",
"ruins": "PMD Erkundungsteam Himmel Tiefes Ruinenverlies",
"plains": "Firel - Route 888",
"power_plant": "Firel - The Klink",
"ruins": "Lmz - Ancient Ruins",
"sea": "Andr06 - Marine Mystique",
"seabed": "Firel - Seabed",
"slum": "Andr06 - Sneaky Snom",
@ -131,7 +131,7 @@
"tall_grass": "PMD Erkundungsteam Himmel Nebelwald",
"temple": "PMD Erkundungsteam Himmel Ägishöhle",
"town": "PMD Erkundungsteam Himmel Zufälliges Dungeon-Theme 3",
"volcano": "PMD Erkundungsteam Himmel Dunsthöhle",
"volcano": "Firel - Twisturn Volcano",
"wasteland": "PMD Erkundungsteam Himmel Verborgenes Hochland",
"encounter_ace_trainer": "SW Trainerblicke treffen sich (Ass-Trainer)",
"encounter_backpacker": "SW Trainerblicke treffen sich (Backpacker)",
@ -149,5 +149,11 @@
"encounter_youngster": "SW Trainerblicke treffen sich (Knirps)",
"heal": "SW Pokémon-Heilung",
"menu": "PMD Erkundungsteam Himmel Willkommen in der Welt der Pokémon!",
"title": "PMD Erkundungsteam Himmel Top-Menü-Thema"
"title": "PMD Erkundungsteam Himmel Top-Menü-Thema",
"mystery_encounter_weird_dream": "PMD Erkundungsteam Himmel Zeitturmspitze",
"mystery_encounter_fun_and_games": "PMD Erkundungsteam Himmel Gildenmeister Knuddeluff",
"mystery_encounter_gen_5_gts": "SW GTS",
"mystery_encounter_gen_6_gts": "XY GTS",
"mystery_encounter_delibirdy": "Firel - DeliDelivery!"
}

View File

@ -53,7 +53,49 @@ import terrain from "./terrain.json";
import modifierSelectUiHandler from "./modifier-select-ui-handler.json";
import moveTriggers from "./move-trigger.json";
import runHistory from "./run-history.json";
import mysteryEncounterMessages from "./mystery-encounter-messages.json";
import lostAtSea from "./mystery-encounters/lost-at-sea-dialogue.json";
import mysteriousChest from "./mystery-encounters/mysterious-chest-dialogue.json";
import mysteriousChallengers from "./mystery-encounters/mysterious-challengers-dialogue.json";
import darkDeal from "./mystery-encounters/dark-deal-dialogue.json";
import departmentStoreSale from "./mystery-encounters/department-store-sale-dialogue.json";
import fieldTrip from "./mystery-encounters/field-trip-dialogue.json";
import fieryFallout from "./mystery-encounters/fiery-fallout-dialogue.json";
import fightOrFlight from "./mystery-encounters/fight-or-flight-dialogue.json";
import safariZone from "./mystery-encounters/safari-zone-dialogue.json";
import shadyVitaminDealer from "./mystery-encounters/shady-vitamin-dealer-dialogue.json";
import slumberingSnorlax from "./mystery-encounters/slumbering-snorlax-dialogue.json";
import trainingSession from "./mystery-encounters/training-session-dialogue.json";
import theStrongStuff from "./mystery-encounters/the-strong-stuff-dialogue.json";
import pokemonSalesman from "./mystery-encounters/the-pokemon-salesman-dialogue.json";
import offerYouCantRefuse from "./mystery-encounters/an-offer-you-cant-refuse-dialogue.json";
import delibirdy from "./mystery-encounters/delibirdy-dialogue.json";
import absoluteAvarice from "./mystery-encounters/absolute-avarice-dialogue.json";
import aTrainersTest from "./mystery-encounters/a-trainers-test-dialogue.json";
import trashToTreasure from "./mystery-encounters/trash-to-treasure-dialogue.json";
import berriesAbound from "./mystery-encounters/berries-abound-dialogue.json";
import clowningAround from "./mystery-encounters/clowning-around-dialogue.json";
import partTimer from "./mystery-encounters/part-timer-dialogue.json";
import dancingLessons from "./mystery-encounters/dancing-lessons-dialogue.json";
import weirdDream from "./mystery-encounters/weird-dream-dialogue.json";
import theWinstrateChallenge from "./mystery-encounters/the-winstrate-challenge-dialogue.json";
import teleportingHijinks from "./mystery-encounters/teleporting-hijinks-dialogue.json";
import bugTypeSuperfan from "./mystery-encounters/bug-type-superfan-dialogue.json";
import funAndGames from "./mystery-encounters/fun-and-games-dialogue.json";
import uncommonBreed from "./mystery-encounters/uncommon-breed-dialogue.json";
import globalTradeSystem from "./mystery-encounters/global-trade-system-dialogue.json";
import expertPokemonBreeder from "./mystery-encounters/the-expert-pokemon-breeder-dialogue.json";
/**
* Dialogue/Text token injection patterns that can be used:
* - `$` will be treated as a new line for Message and Dialogue strings.
* - `@d{<number>}` will add a time delay to text animation for Message and Dialogue strings.
* - `@s{<sound_effect_key>}` will play a specified sound effect for Message and Dialogue strings.
* - `@f{<number>}` will fade the screen to black for the given duration, then fade back in for Message and Dialogue strings.
* - `{{<token>}}` (MYSTERY ENCOUNTERS ONLY) will auto-inject the matching dialogue token value that is stored in {@link IMysteryEncounter.dialogueTokens}.
* - (see [i18next interpolations](https://www.i18next.com/translation-function/interpolation)) for more details.
* - `@[<TextStyle>]{<text>}` (STATIC TEXT ONLY, NOT USEABLE WITH {@link UI.showText()} OR {@link UI.showDialogue()}) will auto-color the given text to a specified {@link TextStyle} (e.g. `TextStyle.SUMMARY_GREEN`).
*/
export const deConfig = {
ability,
abilityTriggers,
@ -110,4 +152,40 @@ export const deConfig = {
modifierSelectUiHandler,
moveTriggers,
runHistory,
mysteryEncounter: {
// DO NOT REMOVE
"unit_test_dialogue": "{{test}}{{test}} {{test{{test}}}} {{test1}} {{test\}} {{test\\}} {{test\\\}} {test}}",
mysteriousChallengers,
mysteriousChest,
darkDeal,
fightOrFlight,
slumberingSnorlax,
trainingSession,
departmentStoreSale,
shadyVitaminDealer,
fieldTrip,
safariZone,
lostAtSea,
fieryFallout,
theStrongStuff,
pokemonSalesman,
offerYouCantRefuse,
delibirdy,
absoluteAvarice,
aTrainersTest,
trashToTreasure,
berriesAbound,
clowningAround,
partTimer,
dancingLessons,
weirdDream,
theWinstrateChallenge,
teleportingHijinks,
bugTypeSuperfan,
funAndGames,
uncommonBreed,
globalTradeSystem,
expertPokemonBreeder
},
mysteryEncounterMessages
};

View File

@ -1026,6 +1026,116 @@
"1": "Du bist unfassbar stark. Kein Wunder, dass die anderen Bosse gegen dich verloren haben..."
}
},
"stat_trainer_buck": {
"encounter": {
"1": "...Ich sag dir jetzt mal was. Ich bin echt stark. Tue überrascht!",
"2": "Ich fühle, wie meine Pokémon in ihren Pokébällen zittern!"
},
"victory": {
"1": "Hehehehe! So heiß bist du!",
"2": "Hehehehe! So heiß bist du!"
},
"defeat": {
"1": "Whoa! Du scheinst ja wirklich erschöpft zu sein.",
"2": "Whoa! Du scheinst ja wirklich erschöpft zu sein."
}
},
"stat_trainer_cheryl": {
"encounter": {
"1": "Meine Pokémon können es kaum erwarten, zu kämpfen.",
"2": "Ich sollte dich warnen, meine Pokémon können ziemlich wild sein."
},
"victory": {
"1": "Ein gutes Verhältnis von Angriff und Verteidigung... Das ist nicht einfach.",
"2": "Ein gutes Verhältnis von Angriff und Verteidigung... Das ist nicht einfach."
},
"defeat": {
"1": "Brauchen deine Pokémon Heilung?",
"2": "Brauchen deine Pokémon Heilung?"
}
},
"stat_trainer_marley": {
"encounter": {
"1": "...OK. Ich werde mein Bestes geben.",
"2": "...OK. Ich werde nicht verlieren...!"
},
"victory": {
"1": "... Awww.",
"2": "... Awww."
},
"defeat": {
"1": "... Auf Wiedersehen.",
"2": "... Auf Wiedersehen."
}
},
"stat_trainer_mira": {
"encounter": {
"1": "Du wirst von Mira schockiert sein!",
"2": "Mira wird dir zeigen, dass Mira sich nicht mehr verirrt!"
},
"victory": {
"1": "Mira wundern, ob sie in diesem Land weit kommen kann.",
"2": "Mira wundern, ob sie in diesem Land weit kommen kann."
},
"defeat": {
"1": "Mira wuss, dass sie gewinnen würde!",
"2": "Mira wuss, dass sie gewinnen würde!"
}
},
"stat_trainer_riley": {
"encounter": {
"1": "Kämpfe sind unsere Art der Begrüßung.",
"2": "Wir setzen alles daran, deine Pokémon zu besiegen."
},
"victory": {
"1": "Manchmal kämpfen wir, und manchmal schließen wir uns zusammen...\n$Es ist großartig, wie Trainer interagieren können.",
"2": "Manchmal kämpfen wir, und manchmal schließen wir uns zusammen...\n$Es ist großartig, wie Trainer interagieren können."
},
"defeat": {
"1": "Du hast dich gut geschlagen. Bis zum nächsten Mal.",
"2": "Du hast dich gut geschlagen. Bis zum nächsten Mal."
}
},
"winstrates_victor": {
"encounter": {
"1": "Das ist der Kampfgeist den ich sehen will! Ich mag dich!"
},
"victory": {
"1": "Ahh! Du bist stärker als ich dachte!"
}
},
"winstrates_victoria": {
"encounter": {
"1": "Mein Gott! Bist du nicht etwas jung?\n$Du musst ein ziemlich guter Trainer sein, um meinen Mann zu besiegen.\n$Jetzt bin ich wohl an der Reihe!"
},
"victory": {
"1": "Waas? Wie stark bist du denn?"
}
},
"winstrates_vivi": {
"encounter": {
"1": "Du bist stärker als Mama? Wow! Aber ich bin auch stark! Wirklich! Ehrlich!"
},
"victory": {
"1": "Huh? Habe ich wirklich verloren?\nSchnief... Omaaa!"
}
},
"winstrates_vicky": {
"encounter": {
"1": "Wie kannst du es wagen, meine kostbare Enkelin zum Weinen zu bringen!\n$Ich sehe, ich muss dir eine Lektion erteilen.\n$Mach dich bereit, eine Niederlage zu erleiden!"
},
"victory": {
"1": "Wow! So stark!\nMeine Enkelin hat nicht gelogen."
}
},
"winstrates_vito": {
"encounter": {
"1": "Ich habe zusammen mit meiner ganzen Familie trainiert, mit jedem von uns!\n$Ich verliere gegen niemanden!"
},
"victory": {
"1": "Ich war besser als jeder in meiner Familie. Ich habe noch nie verloren..."
}
},
"brock": {
"encounter": {
"1": "Meine Expertise in Bezug auf Gesteins-Pokémon wird dich besiegen! Komm schon!",

View File

@ -11,6 +11,7 @@
"gachaTypeLegendary": "Erhöhte Chance auf legendäre Eier.",
"gachaTypeMove": "Erhöhte Chance auf Eier mit seltenen Attacken.",
"gachaTypeShiny": "Erhöhte Chance auf schillernde Eier.",
"eventType": "Geheimnisvolles Ereignis",
"selectMachine": "Wähle eine Maschine.",
"notEnoughVouchers": "Du hast nicht genug Ei-Gutscheine!",
"tooManyEggs": "Du hast schon zu viele Eier!",

View File

@ -8,5 +8,7 @@
"lockRaritiesDesc": "Setze die Seltenheit der Items fest. (Beeinflusst die Rollkosten).",
"checkTeamDesc": "Überprüfe dein Team or nutze Formänderungsitems.",
"rerollCost": "{{formattedMoney}}₽",
"itemCost": "{{formattedMoney}}₽"
"itemCost": "{{formattedMoney}}₽",
"continueNextWaveButton": "Fortfahren",
"continueNextWaveDescription": "Zur nächsten Welle fortfahren."
}

View File

@ -68,6 +68,20 @@
"BaseStatBoosterModifierType": {
"description": "Erhöht den {{stat}} Basiswert des Trägers um 10%. Das Stapellimit erhöht sich, je höher dein IS-Wert ist."
},
"PokemonBaseStatTotalModifierType": {
"name": "Pottrottsaft",
"description": "{{increaseDecrease}} alle Basiswerte des Trägers um {{statValue}}. Du wurdest von Pottrott {{blessCurse}}.",
"extra": {
"increase": "Erhöht",
"decrease": "Verringert",
"blessed": "gesegnet",
"cursed": "verflucht"
}
},
"PokemonBaseStatFlatModifierType": {
"name": "Spezialität",
"description": "Erhöht den {{stats}}-Wert des Trägers um {{statValue}}. Nach einem komischen Traum gefunden."
},
"AllPokemonFullHpRestoreModifierType": {
"description": "Stellt 100% der KP aller Pokémon her."
},
@ -401,7 +415,13 @@
"ENEMY_FUSED_CHANCE": {
"name": "Fusionsmarke",
"description": "Fügt eine 1%ige Chance hinzu, dass ein wildes Pokémon eine Fusion ist."
}
},
"MYSTERY_ENCOUNTER_SHUCKLE_JUICE": { "name": "Pottrottsaft" },
"MYSTERY_ENCOUNTER_BLACK_SLUDGE": { "name": "Giftschleim", "description": "Der Geruch ist so stark, dass die Geschäfte ihre Items nur zu einem stark erhöhten Preis verkaufen." },
"MYSTERY_ENCOUNTER_MACHO_BRACE": { "name": "Machoschiene", "description": "Das Besiegen eines Pokémon gewährt dem Besitzer einen Machoschiene-Stapel. Jeder Stapel steigert die Werte leicht, mit einem zusätzlichen Bonus bei maximalen Stapeln." },
"MYSTERY_ENCOUNTER_OLD_GATEAU": { "name": "Spezialität", "description": "Erhöht den {{stats}}-Wert des Trägers um {{statValue}}." },
"MYSTERY_ENCOUNTER_GOLDEN_BUG_NET": { "name": "Golden Bug Net", "description": "Erhöht die Chance, dass der Besitzer mehr Pokémon vom Typ Käfer findet. Hat ein seltsames Gewicht." }
},
"SpeciesBoosterItem": {
"LIGHT_BALL": {

View File

@ -65,6 +65,7 @@
"suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!",
"revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!",
"swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!",
"chillyReception": "{{pokemonName}} erzählt einen schlechten Witz, der nicht besonders gut ankommt...",
"exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!",
"safeguard": "{{targetName}} wird durch Bodyguard geschützt!",
"afterYou": "{{targetName}} lässt sich auf Galanterie ein!"

View File

@ -3129,7 +3129,7 @@
},
"auraWheel": {
"name": "Aura-Rad",
"effect": "Mithilfe der in den Backentaschen gespeicherten Energie greift der Anwender an und erhöht seine Initiative. Der Typ der Attacke hängt von Morpekos Form ab."
"effect": "Mithilfe der in den Backentaschen gespeicherten Energie greift der Anwender an und erhöht seine Initiative. Wenn dies von Morpeko verwendet wird hängt der Typ der Attacke von dessen Form ab."
},
"breakingSwipe": {
"name": "Breitseite",

View File

@ -0,0 +1,7 @@
{
"paid_money": "Du bezahlst {{amount, number}} ₽.",
"receive_money": "Du erhältst {{amount, number}} ₽!",
"affects_pokedex": "Beeinflusst Pokédex-Daten",
"cancel_option": "Zurück zur Auswahl der Begegnungsoptionen.",
"view_party_button": "Team überprüfen"
}

View File

@ -0,0 +1,47 @@
{
"intro": "Ein sehr starker Trainer kommt auf dich zu...",
"buck": {
"intro_dialogue": "Yo, Trainer! Mein Name ist Avenaro.$Ich habe ein super Angebot für einen starken Trainer wie dich!$Ich trage zwei seltene Pokémon-Eier bei mir, aber ich möchte, dass sich jemand anderes um eines kümmert.$Wenn du mir beweisen kannst, dass du ein starker Trainer bist, werde ich dir das seltenere Ei geben!",
"accept": "Wohooo! Ich bin Feuer und Flamme!",
"decline": "Manno, es sieht so aus, als wäre dein Team nicht in Bestform.$Hier, lass mich dir helfen."
},
"cheryl": {
"intro_dialogue": "Hallo mein Name ist Raissa, ich habe eine besondere Bitte an dich, einen starken Trainer.$Ich trage zwei seltene Pokémon-Eier bei mir, aber ich möchte, dass sich jemand anderes um eines kümmert.$Wenn du mir beweisen kannst, dass du ein starker Trainer bist, werde ich dir das seltenere Ei geben!",
"accept": "Ich hoffe, du bist bereit!",
"decline": "Ich verstehe, es sieht so aus, als wäre dein Team nicht in der besten Verfassung.$Hier, lass mich dir helfen."
},
"marley": {
"intro_dialogue": "...@d{64} Ich bin Charlie.$Ich habe ein Angebot für dich...$Ich trage zwei Pokémon-Eier bei mir, aber ich möchte, dass sich jemand anderes um eines kümmert.$Wenn du stärker bist als ich, werde ich dir das seltenere Ei geben.",
"accept": "...So ist das also.",
"decline": "...Deine Pokémon sehen verletzt aus...Lass mich helfen."
},
"mira": {
"intro_dialogue": "Hi, ich bin Orisa!$Ich habe eine Bitte an dich, einen starken Trainer.$Ich trage zwei seltene Pokémon-Eier bei mir, aber ich möchte, dass sich jemand anderes um eines kümmert.$Wenn du mir beweisen kannst, dass du ein starker Trainer bist, werde ich dir das seltenere Ei geben!",
"accept": "Du wirst Orisa herausfordern? Juhu!",
"decline": "Aww, kein Kampf? Das ist okay!$Hier, Orisa wird dein Team heilen!"
},
"riley": {
"intro_dialogue": "Ich Urs, ich habe eine Bitte an dich, einen starken Trainer.$Ich trage zwei seltene Pokémon-Eier bei mir, aber ich möchte, dass sich jemand anderes um eines kümmert.$Wenn du mir beweisen kannst, dass du ein starker Trainer bist, werde ich dir das seltenere Ei geben!",
"accept": "Dieser Blick...Lass uns das machen.",
"decline": "Ich verstehe, dein Team sieht geschlagen aus.$Hier, lass mich dir helfen."
},
"title": "Ein Trainer-Test",
"description": "Es scheint als würde dieser Trainer dir ein Ei geben, egal wie du dich entscheidest. Wenn du es jedoch schaffst, diesen starken Trainer zu besiegen, wirst du ein viel selteneres Ei erhalten.",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Die Herausforderung annehmen",
"tooltip": "(-) Schwerer Kampf\n(+) Erhalte ein @[TOOLTIP_TITLE]{Sehr seltenes Ei}"
},
"2": {
"label": "Die Herausforderung ablehnen",
"tooltip": "(+) Team wird geheilt\n(+) Erhalte ein @[TOOLTIP_TITLE]{Ei}"
}
},
"eggTypes": {
"rare": "seltenes Ei",
"epic": "episches Ei",
"legendary": "legendäres Ei"
},
"outro": "{{statTrainerName}} gibt dir ein {{eggType}}!"
}

View File

@ -0,0 +1,25 @@
{
"intro": "Ein {{greedentName}} überfällt dich und stiehlt die Beeren deines Teams!",
"title": "Absoluter Geiz",
"description": "Der {{greedentName}} hat dich total überrascht und all deine Beeren gestohlen!\nEs sieht so aus, als ob das {{greedentName}} sie gleich essen würde, aber dann hält es inne und sieht dich interessiert an.",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Kampf beginnen",
"tooltip": "(-) Schwerer Kampf\n(+) Belohnungen aus seinem Beerenversteck",
"selected": "Der {{greedentName}} füllt seine Backen und bereitet sich auf den Kampf vor!",
"boss_enraged": "{{greedentName}} Liebe für Essen hat es aufgebracht!",
"food_stash": "Es scheint, als ob das {{greedentName}} ein riesiges Nahrungslager bewacht hat!$Jedes Pokémon in deinem Team erhält {{foodReward}}!"
},
"2": {
"label": "Verhandeln",
"tooltip": "(+) Einige Beeren zurückbekommen",
"selected": "Deine Bitte berührt das {{greedentName}}.$Es gibt dir nicht alle Beeren zurück, aber wirft dir trotzdem ein paar zu."
},
"3": {
"label": "Beeren überlassen",
"tooltip": "(-) Alle Beeren verlieren\n(?) Das {{greedentName}} wird dich mögen",
"selected": "Das {{greedentName}} verschlingt den gesamten Beerenversteck in einem Blitz!$Es klopft sich auf den Bauch und sieht dich dankbar an.$Vielleicht könntest du ihm auf deinem Abenteuer mehr Beeren geben...$@s{level_up_fanfare}Das {{greedentName}} möchte sich deiner Gruppe anschließen!"
}
}
}

View File

@ -0,0 +1,26 @@
{
"intro": "Du wirst von einem reich aussehenden Jungen aufgehalten.",
"speaker": "Reicher Junge",
"intro_dialogue": "Guten Tag!$Ich kann nicht anders, als zu bemerken, dass dein\n{{strongestPokemon}} einfach göttlich aussieht!$Ich habe schon immer ein Pokémon wie dieses haben wollen!$Ich würde es dir großzügig bezahlen, und dir auch diesen alten Kram geben!",
"title": "Ein Angebot das du nicht ablehnen kannst",
"description": "Dir wird ein @[TOOLTIP_TITLE]{Schillerpin} und {{price, money}} für dein {{strongestPokemon}} angeboten!\nEs ist ein extrem gutes Angebot, aber kannst du es wirklich ertragen, dich von einem so starken Teammitglied zu trennen?",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Den Deal annehmen",
"tooltip": "(-) Verliere {{strongestPokemon}}\n(+) Erhalte einen @[TOOLTIP_TITLE]{Schillerpin}\n(+) Erhalte {{price, money}}",
"selected": "Wunderbar!@d{32} Komm mit, {{strongestPokemon}}!$Es ist Zeit, dich allen im Yachtclub zu zeigen!$Die werden so neidisch sein!"
},
"2": {
"label": "Das Kind erpressen",
"tooltip": "(+) {{option2PrimaryName}} setzt {{moveOrAbility}} ein\n(+) Erhalte {{price, money}}",
"tooltip_disabled": "Dein Pokémon muss bestimmte Attacken oder Fähigkeiten haben, um diese Option zu wählen",
"selected": "Mein Gott, wir werden ausgeraubt, {{liepardName}}!$Du wirst von meinen Anwälten hören!"
},
"3": {
"label": "Weggehen",
"tooltip": "(-) Keine Belohnung",
"selected": "Was ein beschissener Tag...$Ach, was solls. Lass uns zurück zum Yachtclub gehen, {{liepardName}}."
}
}
}

View File

@ -0,0 +1,26 @@
{
"intro": "Da ist ein riesiger Beerenstrauch in der Nähe dieses Pokémons!",
"title": "Überall Beeren",
"description": "Es scheint, als ob ein starkes Pokémon einen Beerenstrauch bewacht. Ein Kampf wäre der direkte Weg, aber es sieht stark aus. Vielleicht könnte ein schnelles Pokémon ein paar Beeren schnappen, ohne erwischt zu werden?",
"query": "Was wirst du tun?",
"berries": "Berren!",
"option": {
"1": {
"label": "Kampf beginnen",
"tooltip": "(-) Schwerer Kampf\n(+) Beeren erhalten",
"selected": "Du trittst dem Pokémon ohne Furcht entgegen."
},
"2": {
"label": "Zum Strauch rennen",
"tooltip": "(-) {{fastestPokemon}} nutzt seine Geschwindigkeit\n(+) Beeren erhalten",
"selected": "Dein {{fastestPokemon}} rennt zum Strauch!$Es schafft es, {{numBerries}} zu schnappen, bevor das {{enemyPokemon}} reagieren kann!$Du ziehst dich schnell mit deiner neuen Beute zurück.",
"selected_bad": "Dein {{fastestPokemon}} rennt zum Strauch!$Oh nein! Das {{enemyPokemon}} war schneller und hat den Weg blockiert!",
"boss_enraged": "Das gegnerische {{enemyPokemon}} ist wütend geworden!"
},
"3": {
"label": "Verlassen",
"tooltip": "(-) Keine Belohnung",
"selected": "Du lässt das starke Pokémon mit seinem Item zurück und gehst weiter."
}
}
}

View File

@ -0,0 +1,40 @@
{
"intro": "Ein ungewöhnlicher Trainer mit allerlei Käfer-Schnickschnack versperrt dir den Weg!",
"intro_dialogue": "Hey, Trainer! Ich bin auf einer Mission, um die seltensten Käfer-Pokémon zu finden!$Du musst Käfer-Pokémon auch lieben, oder? Jeder liebt Käfer-Pokémon!",
"title": "Der Käfersammler-Superfan",
"speaker": "Käfersammler-Superfan",
"description": "Der Trainer plappert drauf los, ohne auf eine Antwort zu warten...\nEs scheint, als gäbe es nur einen Weg, um aus dieser Situation herauszukommen... Die Aufmerksamkeit des Trainers zu erregen!",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Pokémon-Kampf",
"tooltip": "(-) Herausfordernder Kampf\n(+) Einem Pokémon eine Käfer-Attacke beibringen",
"selected": "Ein Pokémon-Kampf? Meine Käfer-Pokémon sind mehr als bereit für dich!"
},
"2": {
"label": "Käfer-Pokémon zeigen",
"tooltip": "(+) Erhalte ein Geschenk",
"disabled_tooltip": "Du brauchst mindestens 1 Käfer-Pokémon in deinem Team, um das auszuwählen.",
"selected": "Du zeigst dem Trainer all deine Käfer-Pokémon...",
"selected_0_to_1": "Huh? Du hast nur {{numBugTypes}} Käfer-Pokémon...$Ich verschwende hier meine Zeit...",
"selected_2_to_3": "Hey, du hast {{numBugTypes}}! Nicht schlecht.$Hier, das könnte dir auf deiner Reise helfen, mehr zu fangen!",
"selected_4_to_5": "Was? Du hast {{numBugTypes}}? Nicht schlecht!$Du bist noch nicht ganz auf meinem Level, aber ich kann mich in dir erkennen! $Nimm das, mein junger Padawan!",
"selected_6": "Wow! {{numBugTypes}}!$Du musst Käfer-Pokémon fast so sehr lieben wie ich!$Hier, nimm das als Zeichen unserer Kameradschaft!"
},
"3": {
"label": "Verschenke ein Käfer-Item",
"tooltip": "(-) Du gibst dem Trainer ein {{requiredBugItems}}\n(+) Erhalte ein Geschenk",
"disabled_tooltip": "Du brauchst ein {{requiredBugItems}}, um das auszuwählen.",
"select_prompt": "Wählen Sie ein Item aus, um es zu verschenken.",
"invalid_selection": "Das Pokémon hat kein solches Item.",
"selected": "Du gibst {{selectedItem}} an dem Trainer .",
"selected_dialogue": "Wow! {{selectedItem}}, für mich? Du bist nicht so schlecht, Junge!$Als Zeichen meiner Anerkennung möchte ich, dass du dieses besondere Geschenk bekommst!$Es wurde in meiner Familie weitergegeben, und jetzt möchte ich, dass du es hast!"
}
},
"battle_won": "Dein Wissen und Können waren perfekt, um unsere Schwächen auszunutzen!$Als Gegenleistung für die wertvolle Lektion, erlaube mir, einem deiner Pokémon eine Käfer-Attacke beizubringen!",
"teach_move_prompt": "Wähle eine Attacke aus die du deinem Pokémon beibringen möchtest.",
"confirm_no_teach": "Bist du sicher, dass du keine dieser großartigen Attacken lernen möchtest?",
"outro": "Ich sehe großartige Käfer-Pokémon in deiner Zukunft! Mögen sich unsere Wege wieder kreuzen!$Mach's gut!",
"numBugTypes_one": "{{count}} Käfer-Pokémon",
"numBugTypes_other": "{{count}} Käfer-Pokémon"
}

View File

@ -0,0 +1,35 @@
{
"intro": "Es ist...@d{64} ein Clown?",
"speaker": "Clown",
"intro_dialogue": "Du tollpatschiger Trottel, bereite dich auf einen brillanten Kampf vor!\nDu wirst von diesem prügelnden Straßenmusikanten besiegt!",
"title": "Rumgeblödel",
"description": "Irgendwas stimmt nicht mit dieser Begegnung. Der Clown scheint darauf aus zu sein, dich zu einem Kampf zu provozieren, aber zu welchem Zweck?\n\nDas {{blacephalonName}} ist besonders seltsam, als hätte es @[TOOLTIP_TITLE]{seltsame Typen} und eine @[TOOLTIP_TITLE]{Fähigkeit.}",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Kampf beginnen",
"tooltip": "(-) Komischer Kampf\n(?) Beeinflusst Pokémon-Fähigkeiten",
"selected": "Deine erbärmlichen Pokémon sind bereit für eine erbärmliche Vorstellung!",
"apply_ability_dialogue": "Eine sensationelle Vorstellung! Dein Können passt zu einer sensationellen Fähigkeit als Beute!",
"apply_ability_message": "Der Clown bietet an, die Fähigkeit eines deiner Pokémon dauerhaft auf {{ability}} zu wechseln!",
"ability_prompt": "Soll eines deiner Pokémon die Fähigkeit {{ability}} dauerhaft erlangen?",
"ability_gained": "@s{level_up_fanfare}{{chosenPokemon}} hat die Fähigkeit {{ability}} erhalten!"
},
"2": {
"label": "Nicht provozieren lassen",
"tooltip": "(-) Der Clown ist beleidigt\n(?) Beeinflusst Pokémon-Items",
"selected": "Du erbärmlicher Feigling, du verweigerst einen wunderbaren Kampf? Fühle meinen Zorn!",
"selected_2": "Das {{blacephalonName}} des Clowns verwendet Trickbetrug! Alle Items deines {{switchPokemon}} wurden zufällig vertauscht!",
"selected_3": "Meine perfekte List hat dich in die Irre geführt!"
},
"3": {
"label": "Die Beleidigungen erwidern",
"tooltip": "(-) Den Clown verärgern\n(?) Beeinflusst Pokémon-Typen",
"selected": "Du erbärmlicher Feigling verweigerst einen wunderbaren Kampf? Fühle meinen Zorn!",
"selected_2": "Das {{blacephalonName}} des Clowns verwendet eine seltsame Attacke! Alle Typen deines Teams wurden zufällig vertauscht!",
"selected_3": "Meine perfekte List hat dich in die Irre geführt!"
}
},
"outro": "Der Clown und seine Kumpanen verschwinden in einer Rauchwolke."
}

View File

@ -0,0 +1,27 @@
{
"intro": "Ein {{oricorioName}} tanzt traurig allein, ohne einen Partner.",
"title": "Tanzstunden",
"description": "Das {{oricorioName}} scheint nicht aggressiv zu sein, im Gegenteil, es scheint traurig zu sein.\nVielleicht möchte es einfach nur mit jemandem tanzen...",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Kampf beginnen",
"tooltip": "(-) Schwerer Kampf\n(+) Erhalte ein Stab",
"selected": "Das {{oricorioName}} ist verstört und verteidigt sich!",
"boss_enraged": "Das {{oricorioName}} ist wütend und steigert seine Werte!"
},
"2": {
"label": "Lerne den Tanz",
"tooltip": "(+) Bringe einem Pokémon Wecktanz bei",
"selected": "Du schaust dem {{oricorioName}} genau zu, wie es seinen Tanz aufführt...$@s{level_up_fanfare}Dein {{selectedPokemon}} hat von {{oricorioName}} gelernt!"
},
"3": {
"label": "Zeig einen Tanz",
"tooltip": "(-) Bringe dem {{oricorioName}} einen Tanz bei\n(+) Das {{oricorioName}} wird dich mögen",
"disabled_tooltip": "Dein Pokémon muss einen Tanz beherrschen, um diese Option zu wählen.",
"select_prompt": "Wählen Sie eine Tanzattacke aus, die verwendet werden soll.",
"selected": "Das {{oricorioName}} schaut fasziniert zu, wie {{selectedPokemon}} {{selectedMove}} vorführt!$Es liebt die Vorführung!$@s{level_up_fanfare}Das {{oricorioName}} möchte sich dir anschließen!"
}
},
"invalid_selection": "Das Pokémon kennt keine Tanzattacke"
}

View File

@ -0,0 +1,24 @@
{
"intro": "Ein seltsamer Mann in einem zerrissenen Mantel steht dir im Weg...",
"speaker": "Seltsamer Mann",
"intro_dialogue": "Hey, du!$Ich habe an einem neuen Gerät gearbeitet, um die verborgene Kraft eines Pokémon zum Vorschein zu bringen!$Es bindet die Atome des Pokémon auf molekularer Ebene vollständig neu und bringt sie in eine$weitaus mächtigere Form.$Hehe...@d{64} Ich brauche nur ein paar Opf-@d{32} Ähm, Testpersonen, um zu beweisen, dass es funktioniert.",
"title": "Dunkler Handel",
"description": "Der verstörende Typ hält einige Pokébälle hoch.\n\"Es wird such für dich lohnen! Du kannst diese tollen Pokébälle als Bezahlung haben, alles was ich brauche ist ein Pokémon aus deinem Team! Hehe...\"",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Aktzeptieren",
"tooltip": "(+) 5 Roguebälle\n(?) Ein zufälliges Pokémon wird verbessert",
"selected_dialogue": "Lass mich mal sehen...${{pokeName}} ist eine gute Wahl!$Denk dran, ich bin nicht verantwortlich, wenn etwas schief geht!@d{32} Hehe...",
"selected_message": "Der Mann übergibt dir 5 Roguebälle.${{pokeName}} springt in die seltsame Maschine...$Blinkende Lichter und seltsame Geräusche kommen aus der Maschine!$...@d{96} Etwas kommt aus der Maschine,\nwütend und wild!"
},
"2": {
"label": "Ablehnen",
"tooltip": "(-) Keine Belohnung",
"selected": "Du willst einem armen Kerl nicht helfen? Pah!"
}
},
"outro": "Nach der schrecklichen Begegnung, sammelst du dich und gehst weiter."
}

View File

@ -0,0 +1,29 @@
{
"intro": "Ein Schwarm {{delibirdName}} ist aufgetaucht!",
"title": "Botogel-Bande",
"description": "Die {{delibirdName}} schauen dich erwartungsvoll an, als ob sie etwas wollen. Vielleicht würde es sie zufriedenstellen, wenn du ihnen ein Item oder etwas Geld gibst?",
"query": "Was möchtest du ihnen geben?",
"invalid_selection": "Das Pokémon hat kein solches Item.",
"option": {
"1": {
"label": "Geld geben",
"tooltip": "(-) Den {{delibirdName}} {{money, money}} geben\n(+) Erhalte ein Geschenk",
"selected": "Du wirfst das Geld zu den {{delibirdName}}, die aufgeregt miteinander schnattern.$Sie drehen sich zu dir um und geben dir glücklich ein Geschenk!"
},
"2": {
"label": "Futter geben",
"tooltip": "(-) Gib den {{delibirdName}} eine Beere oder einen Belebersamen\n(+) Erhalte ein Geschenk",
"select_prompt": "Wähle ein Item aus.",
"selected": "Du wirfst {{chosenItem}} zu den {{delibirdName}}, die aufgeregt miteinander schnattern.$Sie drehen sich zu dir um und geben dir glücklich ein Geschenk!"
},
"3": {
"label": "Ein Item geben",
"tooltip": "(-) Gebe den {{delibirdName}} ein Item\n(+) Erhalte ein Geschenk",
"select_prompt": "Wähle ein Item aus.",
"selected": "Du wirfst {{chosenItem}} zu den {{delibirdName}}, die aufgeregt miteinander schnattern.$Sie drehen sich zu dir um und geben dir glücklich ein Geschenk!"
}
},
"outro": "Die {{delibirdName}} watscheln glücklich davon.$Was für ein seltsamer kleiner Austausch!"
}

View File

@ -0,0 +1,27 @@
{
"intro": "Es ist eine Dame mit vielen Einkaufstüten.",
"speaker": "Einkäuferin",
"intro_dialogue": "Hallo! Bist du auch wegen der tollen Angebote hier?$Es gibt einen speziellen Gutschein, den du während des Verkaufs einlösen kannst!$Ich habe einen zusätzlichen. Hier, bitte!",
"title": "Einkaufszentrum-Verkauf",
"description": "Es gibt Angebote in jede Richtung! Es sieht so aus, als ob es 4 Kassen gibt, an denen du den Gutschein gegen verschiedene Artikel eintauschen kannst. Die Möglichkeiten sind endlos!",
"query": "Welche Kasse wählst du?",
"option": {
"1": {
"label": "TM-Kasse",
"tooltip": "(+) TM Shop"
},
"2": {
"label": "Nährstoff-Kasse",
"tooltip": "(+) Nährstoff Shop"
},
"3": {
"label": "Kampf-Item-Kasse",
"tooltip": "(+) X-Item Shop"
},
"4": {
"label": "Pokéball-Kasse",
"tooltip": "(+) Pokéball Shop"
}
},
"outro": "Was für ein Schnäppchen! Du solltest öfter hier einkaufen."
}

View File

@ -0,0 +1,31 @@
{
"intro": "Eine Lehrerin und ein paar Schulkinder stehen auf einmal vor dir!",
"speaker": "Lehrerin",
"intro_dialogue": "Hallo! Könntest du eine Minute für meine Schüler erübrigen?$Ich bringe ihnen gerade bei, wie Pokémon-Attacken funktionieren und würde ihnen gerne$eine Demonstration zeigen.$Würdest du uns eine Attacke deines Pokémon vorführen?",
"title": "Exkursion",
"description": "Eine Lehrerin fragt nach einer Attackenvorführung eines Pokémon. Je nachdem, welche Attacke du wählst, hat sie vielleicht etwas Nützliches für dich als Belohnung.",
"query": "Welchen Attacken-Typ wählst du?",
"option": {
"1": {
"label": "Physische Attacke",
"tooltip": "(+) Physische Item-Belohnungen"
},
"2": {
"label": "Spezielle Attacke",
"tooltip": "(+) Spezielle Item-Belohnungen"
},
"3": {
"label": "Status-Attacke",
"tooltip": "(+) Status Item-Belohnungen"
},
"selected": "{{pokeName}} zeigt eine beeindruckende Vorführung von {{move}}!"
},
"second_option_prompt": "Wähle eine Attacke die dein Pokémon einsetzen soll.",
"incorrect": "...$Das ist keine {{moveCategory}}Attacke!\nEs tut mir leid, aber ich kann dir nichts geben.$Kommt Kinder, wir suchen uns woanders einen besseren Trainer.",
"incorrect_exp": "Es scheint, als hättest du eine wertvolle Lektion gelernt?$Dein Pokémon hat auch etwas Erfahrung gesammelt.",
"correct": "Ich dank dir vielmals für deine Freundlichkeit!$Ich hoffe, diese Items sind nützlich für dich.",
"correct_exp": "{{pokeName}} hat auch etwas wertvolle Erfahrung gesammelt!",
"status": "Status-",
"physical": "physische ",
"special": "spezielle "
}

View File

@ -0,0 +1,26 @@
{
"intro": "Du hast einen Sturm aus Rauch und Asche entdeckt!",
"title": "Feurige Folgen",
"description": "Die umherwirbelnde Asche und Glut haben die Sicht auf fast Null reduziert. Es scheint, als könnte es eine... Quelle geben, die diese Bedingungen verursacht. Aber was könnte hinter einem Phänomen dieser Größe stecken?",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Finde die Quelle",
"tooltip": "(?) Entdecke die Quelle\n(-) Schwieriger Kampf",
"selected": "Du hast die Quelle des Sturms gefunden!$Es sind zwei {{volcaronaName}}, die in der Mitte eines Paarungstanzes sind!$Sie nehmen die Unterbrechung nicht gut auf und greifen an!"
},
"2": {
"label": "Sich einigeln",
"tooltip": "(-) Die Folgen des Wetters erleiden",
"selected": "Die Folgen des Wetters sind verheerend!$Deine Pokémon nehmen 20% ihrer maximalen KP als Schaden!",
"target_burned": "Dein {{burnedPokemon}} wurde auch verbrannt!"
},
"3": {
"label": "Dein Feuer-Pokémon hilft",
"tooltip": "(+) Das Wetter klärt auf\n(+) Erhalte ein Holzkohle",
"disabled_tooltip": "Du benötigst mindestens 2 Feuer-Pokémon, um diese Option auszuwählen",
"selected": "Dein {{option3PrimaryName}} und {{option3SecondaryName}} führen dich zu zwei {{volcaronaName}}, die in der Mitte eines Paarungstanzes sind!$Zum Glück können deine Pokémon sie beruhigen,und sie ziehen ohne Probleme ab."
}
},
"found_charcoal": "Nachdem das Wetter aufklart, entdeckt dein {{leadPokemon}} etwas auf dem Boden.$@s{item_fanfare}{{leadPokemon}} erhält eine Holzkohle!"
}

View File

@ -0,0 +1,25 @@
{
"intro": "Etwas Glänzendes liegt auf dem Boden in der Nähe dieses Pokémons!",
"title": "Kampf oder Flucht",
"description": "Es scheint, als würde ein starkes Pokémon ein Item bewachen. Ein Kampf wäre der direkte Weg, aber es sieht stark aus. Vielleicht könntest du das Item stehlen, wenn du das richtige Pokémon für den Job hast.",
"query": "Was wirst du tun?",
"option": {
"1": {
"label": "Kampf beginnen",
"tooltip": "(-) Schwerer Kampf\n(+) Neues Item",
"selected": "Du trittst dem Pokémon ohne Furcht entgegen.",
"stat_boost": "Die Stärke von {{enemyPokemon}} erhöht einen seiner Werte!"
},
"2": {
"label": "Das Item stehlen",
"disabled_tooltip": "Dein Pokémon muss eine bestimmte Attacken beherrschen, um diese Option zu wählen.",
"tooltip": "(+) {{option2PrimaryName}} setzt {{option2PrimaryMove}} ein",
"selected": ".@d{32}.@d{32}.@d{32}$Dein {{option2PrimaryName}} hilft dir und setzt {{option2PrimaryMove}} ein!$Du hast das Item gestohlen!"
},
"3": {
"label": "Verlassen",
"tooltip": "(-) Keine Belohnung",
"selected": "Du lässt das starke Pokémon mit seinem Item zurück und gehst weiter."
}
}
}

View File

@ -0,0 +1,30 @@
{
"intro_dialogue": "Kommen Sie näher, meine Damen und Herren!$Versuchen Sie Ihr Glück mit dem brandneuen {{wobbuffetName}}-Hau-den-Lukas!",
"speaker": "Animateur",
"title": "Spaß und Spiele!",
"description": "Du hast ein {{wobbuffetName}} gefunden, das ein Spiel spielt! Du hast @[TOOLTIP_TITLE]{3 Züge}, um das {{wobbuffetName}} so nah wie möglich an @[TOOLTIP_TITLE]{1 KP} heranzubringen, @[TOOLTIP_TITLE]{ohne es zu besiegen}, damit es eine riesige Gegenattacke auf der Glockenmaschine ausführen kann.\nAber sei vorsichtig! Wenn du das {{wobbuffetName}} besiegst, musst du die Kosten für die Wiederbelebung bezahlen!",
"query": "Möchtest du spielen?",
"option": {
"1": {
"label": "Das Spiel spielen",
"tooltip": "(-) Zahle {{option1Money, money}}\n(+) Spiele {{wobbuffetName}} Hau-den-Lukas",
"selected": "Zeit dein Glück herauszufordern!"
},
"2": {
"label": "Weggehen",
"tooltip": "(-) Keine Belohnung",
"selected": "Du beeilst dich auf deinem Weg, mit einem leichten Gefühl der Reue."
}
},
"ko": "Oh nein! Das {{wobbuffetName}} ist ohnmächtig geworden!$Du verlierst das Spiel und musst die Kosten für die Wiederbelebung bezahlen...",
"charging_continue": "Das {{wobbuffetName}} lädt seine Gegenattacke auf!",
"turn_remaining_3": "Drei Runden verbleiben!",
"turn_remaining_2": "Zwei Runden verbleiben!",
"turn_remaining_1": "Nur noch eine Runde!",
"end_game": "Die Zeit ist um!$Das {{wobbuffetName}} holt zum Gegenangriff aus und@d{16}.@d{16}.@d{16}.",
"best_result": "Das {{wobbuffetName}} schlägt so hart auf den Knopf, dass die Glocke vom oberen Teil abbricht!$Du gewinnst den Hauptpreis!",
"great_result": "Das {{wobbuffetName}} schlägt den Knopf so hart, dass die Glocke fast getroffen wird!$So nah! Du gewinnst den zweiten Preis!",
"good_result": "Das {{wobbuffetName}} trifft den Knopf stark genug, um die Hälfte der Skala zu erreichen!$Du verdienst den dritten Preis!",
"bad_result": "Das {{wobbuffetName}} trifft den Knopf kaum und nichts passiert...$Oh nein! Du gewinnst nichts!",
"outro": "Das war ein lustiges kleines Spiel!"
}

Some files were not shown because too many files have changed in this diff Show More