mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-01 05:52:17 +02:00
more Mystery Encounter bug fixes
This commit is contained in:
parent
1c87532e64
commit
32741835fd
@ -2,7 +2,7 @@ import Phaser from "phaser";
|
|||||||
import UI from "./ui/ui";
|
import UI from "./ui/ui";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
|
||||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
|
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 * 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 { 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";
|
import { PokeballType } from "./data/pokeball";
|
||||||
@ -1201,32 +1201,12 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
// Check for mystery encounter
|
// Check for mystery encounter
|
||||||
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
|
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
|
||||||
const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves();
|
if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex, mysteryEncounterType)) {
|
||||||
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
|
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
||||||
const roll = Utils.randSeedInt(MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT);
|
// Reset base spawn weight
|
||||||
|
this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_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
|
} else if (newBattleType === BattleType.WILD) {
|
||||||
const sessionEncounterRate = this.mysteryEncounterSaveData.encounterSpawnChance;
|
this.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS;
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1267,9 +1247,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
|
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||||
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
|
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
|
||||||
this.currentBattle.double = false;
|
this.currentBattle.double = false;
|
||||||
this.executeWithSeedOffset(() => {
|
// Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome
|
||||||
this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounterType);
|
this.currentBattle.mysteryEncounterType = mysteryEncounterType;
|
||||||
}, this.currentBattle.waveIndex << 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
|
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
|
||||||
@ -3097,6 +3076,42 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* Loads or generates a mystery encounter
|
||||||
* @param encounterType used to load session encounter when restarting game, etc.
|
* @param encounterType used to load session encounter when restarting game, etc.
|
||||||
|
@ -18,6 +18,7 @@ import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
|||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { CustomModifierSettings } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
|
||||||
export enum ClassicFixedBossWaves {
|
export enum ClassicFixedBossWaves {
|
||||||
// TODO: other fixed wave battles should be added here
|
// TODO: other fixed wave battles should be added here
|
||||||
@ -88,6 +89,7 @@ export default class Battle {
|
|||||||
public playerFaintsHistory: FaintLogEntry[] = [];
|
public playerFaintsHistory: FaintLogEntry[] = [];
|
||||||
public enemyFaintsHistory: FaintLogEntry[] = [];
|
public enemyFaintsHistory: FaintLogEntry[] = [];
|
||||||
|
|
||||||
|
public mysteryEncounterType?: MysteryEncounterType;
|
||||||
/** If the current battle is a Mystery Encounter, this will always be defined */
|
/** If the current battle is a Mystery Encounter, this will always be defined */
|
||||||
public mysteryEncounter?: MysteryEncounter;
|
public mysteryEncounter?: MysteryEncounter;
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
const numBerries = encounter.misc.numBerries;
|
const numBerries = encounter.misc.numBerries;
|
||||||
|
|
||||||
const doBerryRewards = async () => {
|
const doBerryRewards = () => {
|
||||||
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
scene.playSound("item_fanfare");
|
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
|
// Generate a random berry and give it to the first Pokemon with room for it
|
||||||
for (let i = 0; i < numBerries; i++) {
|
for (let i = 0; i < numBerries; i++) {
|
||||||
await tryGiveBerry(scene);
|
tryGiveBerry(scene);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
if (speedDiff < 1) {
|
if (speedDiff < 1) {
|
||||||
// Caught and attacked by boss, gets +1 to all stats at start of fight
|
// 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`);
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
@ -186,7 +186,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Generate a random berry and give it to the first Pokemon with room for it
|
// Generate a random berry and give it to the first Pokemon with room for it
|
||||||
for (let i = 0; i < numBerries; i++) {
|
for (let i = 0; i < numBerries; i++) {
|
||||||
await tryGiveBerry(scene);
|
tryGiveBerry(scene);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -204,7 +204,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
|
// 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);
|
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1)/0.08), numBerries), 2);
|
||||||
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
|
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
|
||||||
const doFasterBerryRewards = async () => {
|
const doFasterBerryRewards = () => {
|
||||||
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
|
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
|
||||||
|
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
@ -212,7 +212,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)
|
// 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++) {
|
for (let i = 0; i < numBerriesGrabbed; i++) {
|
||||||
await tryGiveBerry(scene, fastestPokemon);
|
tryGiveBerry(scene, fastestPokemon);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||||||
)
|
)
|
||||||
.build();
|
.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 berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
|
||||||
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
|
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
|
||||||
|
|
||||||
@ -254,7 +254,7 @@ async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokem
|
|||||||
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
||||||
|
|
||||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||||
await applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
|
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ async function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokem
|
|||||||
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
||||||
|
|
||||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||||
await applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
|
applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onHover: () => {
|
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 formName = tradePokemon.species.forms?.[pokemon.formIndex]?.formName;
|
||||||
const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : "");
|
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 : "");
|
const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
|
||||||
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
|
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
|
||||||
|
@ -22,6 +22,7 @@ import { getLevelTotalExp } from "#app/data/exp";
|
|||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
|
||||||
/** i18n namespace for encounter */
|
/** i18n namespace for encounter */
|
||||||
const namespace = "mysteryEncounter:weirdDream";
|
const namespace = "mysteryEncounter:weirdDream";
|
||||||
@ -105,7 +106,7 @@ const STANDARD_BST_TRANSFORM_BASE_VALUES: [number, number] = [40, 50];
|
|||||||
export const WeirdDreamEncounter: MysteryEncounter =
|
export const WeirdDreamEncounter: MysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
|
||||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
.withDisallowedChallenges(Challenges.SINGLE_TYPE)
|
.withDisallowedChallenges(Challenges.SINGLE_TYPE, Challenges.SINGLE_GENERATION)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
@ -216,7 +217,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
|||||||
pokemon.levelExp = 0;
|
pokemon.levelExp = 0;
|
||||||
|
|
||||||
pokemon.calculateStats();
|
pokemon.calculateStats();
|
||||||
pokemon.updateInfo();
|
await pokemon.updateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
@ -346,6 +347,9 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
// If the previous pokemon had pokerus, transfer to new pokemon
|
// If the previous pokemon had pokerus, transfer to new pokemon
|
||||||
newPokemon.pokerus = previousPokemon.pokerus;
|
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)
|
// 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) => {
|
newPokemon.ivs = newPokemon.ivs.map((iv, index) => {
|
||||||
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
|
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
|
||||||
@ -358,44 +362,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
|
// 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();
|
newPokemon.generateAndPopulateMoveset();
|
||||||
|
// Store a copy of a "standard" generated moveset for the new pokemon, will be used later for finding a favored move
|
||||||
// Try to find a favored STAB move
|
const newPokemonGeneratedMoveset = newPokemon.moveset;
|
||||||
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];
|
|
||||||
|
|
||||||
newPokemon.moveset = previousPokemon.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
|
// Randomize the second type of the pokemon
|
||||||
// If the pokemon does not normally have a second type, it will gain 1
|
// If the pokemon does not normally have a second type, it will gain 1
|
||||||
@ -412,7 +387,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
|
|
||||||
for (const item of transformation.heldItems) {
|
for (const item of transformation.heldItems) {
|
||||||
item.pokemonId = newPokemon.id;
|
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
|
// 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 +398,12 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
stats.push(baseStats[Stat.ATK] < baseStats[Stat.SPATK] ? Stat.ATK : Stat.SPATK);
|
stats.push(baseStats[Stat.ATK] < baseStats[Stat.SPATK] ? Stat.ATK : Stat.SPATK);
|
||||||
// Def or SpDef
|
// Def or SpDef
|
||||||
stats.push(baseStats[Stat.DEF] < baseStats[Stat.SPDEF] ? Stat.DEF : Stat.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()
|
||||||
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU().generateType(scene.getParty(), [20, stats]);
|
.generateType(scene.getParty(), [20, stats])
|
||||||
|
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
||||||
const modifier = modType?.newModifier(newPokemon);
|
const modifier = modType?.newModifier(newPokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
scene.addModifier(modifier);
|
await scene.addModifier(modifier, false, false, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,13 +411,15 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
newPokemon.passive = previousPokemon.passive;
|
newPokemon.passive = previousPokemon.passive;
|
||||||
|
|
||||||
newPokemon.calculateStats();
|
newPokemon.calculateStats();
|
||||||
newPokemon.initBattleInfo();
|
await newPokemon.updateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// One random pokemon will get its passive unlocked
|
// One random pokemon will get its passive unlocked
|
||||||
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
|
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
|
||||||
if (passiveDisabledPokemon?.length > 0) {
|
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
|
// If at least one new starter was unlocked, play 1 fanfare
|
||||||
@ -451,7 +429,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies {
|
function getTransformedSpecies(originalBst: number, bstSearchRange: [number, number], hasPokemonBstHigherThan600: boolean, hasPokemonBstBetween570And600: boolean, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies {
|
||||||
let newSpecies: PokemonSpecies | undefined;
|
let newSpecies: PokemonSpecies | undefined = undefined;
|
||||||
while (isNullOrUndefined(newSpecies)) {
|
while (isNullOrUndefined(newSpecies)) {
|
||||||
const bstCap = originalBst + bstSearchRange[1];
|
const bstCap = originalBst + bstSearchRange[1];
|
||||||
const bstMin = Math.max(originalBst + bstSearchRange[0], 0);
|
const bstMin = Math.max(originalBst + bstSearchRange[0], 0);
|
||||||
@ -566,3 +544,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;
|
||||||
|
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
|
||||||
|
const eggMoves: Moves[] = speciesEggMoves[speciesRootForm].slice(0);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -311,7 +311,9 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
|
|||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
|
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);
|
const modifier = modType?.newModifier(pokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
await pokemon.scene.addModifier(modifier, false, false, false, true);
|
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) {
|
export function getEncounterPokemonLevelForWave(scene: BattleScene, levelAdditiveModifier: number = 0) {
|
||||||
const currentBattle = scene.currentBattle;
|
const currentBattle = scene.currentBattle;
|
||||||
// Default to use the first generated level from enemyLevels, or generate a new one if it DNE
|
const baseLevel = currentBattle.getLevelForWave();
|
||||||
const baseLevel = currentBattle.enemyLevels && currentBattle.enemyLevels?.length > 0 ? currentBattle.enemyLevels[0] : currentBattle.getLevelForWave();
|
|
||||||
|
|
||||||
// Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier
|
// Add a level scaling modifier that is (+1 level per 10 waves) * levelAdditiveModifier
|
||||||
return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0);
|
return baseLevel + Math.max(Math.round((currentBattle.waveIndex / 10) * levelAdditiveModifier), 0);
|
||||||
|
@ -62,7 +62,13 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
|
|
||||||
const battle = this.scene.currentBattle;
|
const battle = this.scene.currentBattle;
|
||||||
|
|
||||||
// Init Mystery Encounter if there is one
|
// Generate and Init Mystery Encounter
|
||||||
|
if (battle.battleType === BattleType.MYSTERY_ENCOUNTER && !battle.mysteryEncounter) {
|
||||||
|
this.scene.executeWithSeedOffset(() => {
|
||||||
|
const currentSessionEncounterType = battle.mysteryEncounterType;
|
||||||
|
battle.mysteryEncounter = this.scene.getMysteryEncounter(currentSessionEncounterType);
|
||||||
|
}, battle.waveIndex << 4);
|
||||||
|
}
|
||||||
const mysteryEncounter = battle.mysteryEncounter;
|
const mysteryEncounter = battle.mysteryEncounter;
|
||||||
if (mysteryEncounter) {
|
if (mysteryEncounter) {
|
||||||
// If ME has an onInit() function, call it
|
// If ME has an onInit() function, call it
|
||||||
@ -152,13 +158,10 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
if (battle.battleType === BattleType.TRAINER) {
|
if (battle.battleType === BattleType.TRAINER) {
|
||||||
loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct?
|
loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct?
|
||||||
} else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
} else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||||
if (!battle.mysteryEncounter) {
|
if (battle.mysteryEncounter?.introVisuals) {
|
||||||
battle.mysteryEncounter = this.scene.getMysteryEncounter(mysteryEncounter?.encounterType);
|
|
||||||
}
|
|
||||||
if (battle.mysteryEncounter.introVisuals) {
|
|
||||||
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));
|
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));
|
||||||
}
|
}
|
||||||
if (battle.mysteryEncounter.loadAssets.length > 0) {
|
if (battle.mysteryEncounter?.loadAssets && battle.mysteryEncounter.loadAssets.length > 0) {
|
||||||
loadEnemyAssets.push(...battle.mysteryEncounter.loadAssets);
|
loadEnemyAssets.push(...battle.mysteryEncounter.loadAssets);
|
||||||
}
|
}
|
||||||
// Load Mystery Encounter Exclamation bubble and sfx
|
// Load Mystery Encounter Exclamation bubble and sfx
|
||||||
|
Loading…
Reference in New Issue
Block a user