mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-04-23 10:05:36 +01:00
various ME bug fixes and balance changes
This commit is contained in:
parent
e23d40618f
commit
b032a3d6a5
File diff suppressed because it is too large
Load Diff
@ -95,10 +95,9 @@ import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase
|
|||||||
import { TurnInitPhase } from "./phases/turn-init-phase";
|
import { TurnInitPhase } from "./phases/turn-init-phase";
|
||||||
import { ShopCursorTarget } from "./enums/shop-cursor-target";
|
import { ShopCursorTarget } from "./enums/shop-cursor-target";
|
||||||
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter from "./data/mystery-encounters/mystery-encounter";
|
||||||
import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
||||||
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||||
|
|
||||||
@ -1151,33 +1150,36 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove these once ME spawn rates are finalized
|
// TODO: remove these once ME spawn rates are finalized
|
||||||
// let testStartingWeight = 0;
|
// let testStartingWeight = 3;
|
||||||
// while (testStartingWeight < 3) {
|
// while (testStartingWeight < 4) {
|
||||||
// calculateMEAggregateStats(this, testStartingWeight);
|
// calculateMEAggregateStats(this, testStartingWeight);
|
||||||
// testStartingWeight += 2;
|
// testStartingWeight += 1;
|
||||||
// }
|
// }
|
||||||
// calculateRareSpawnAggregateStats(this, 14);
|
// calculateRareSpawnAggregateStats(this, 14);
|
||||||
|
|
||||||
// Check for mystery encounter
|
// Check for mystery encounter
|
||||||
// Can only occur in place of a standard wild battle, waves 10-180
|
// Can only occur in place of a standard (non-boss) wild battle, waves 10-180
|
||||||
const highestMysteryEncounterWave = 180;
|
const highestMysteryEncounterWave = 180;
|
||||||
const lowestMysteryEncounterWave = 10;
|
const lowestMysteryEncounterWave = 10;
|
||||||
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
|
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < highestMysteryEncounterWave && newWaveIndex > lowestMysteryEncounterWave) {
|
||||||
const roll = Utils.randSeedInt(256);
|
const roll = Utils.randSeedInt(256);
|
||||||
|
|
||||||
// Base spawn weight is 1/256, and increases by 5/256 for each missed attempt at spawning an encounter on a valid floor
|
// 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 = !isNullOrUndefined(this.mysteryEncounterData?.encounterSpawnChance) ? this.mysteryEncounterData.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
const sessionEncounterRate = this.mysteryEncounterData.encounterSpawnChance;
|
||||||
|
const encounteredEvents = this.mysteryEncounterData.encounteredEvents;
|
||||||
|
|
||||||
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn
|
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn (reverse as well)
|
||||||
// Do the reverse as well
|
// Reduces occurrence of runs with total encounters significantly different from AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
||||||
// Reduces occurrence of runs with very few (<6) and a ton (>10) of encounters
|
|
||||||
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (newWaveIndex - lowestMysteryEncounterWave);
|
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (highestMysteryEncounterWave - lowestMysteryEncounterWave) * (newWaveIndex - lowestMysteryEncounterWave);
|
||||||
const currentRunDiffFromAvg = expectedEncountersByFloor - (this.mysteryEncounterData?.encounteredEvents?.length || 0);
|
const currentRunDiffFromAvg = expectedEncountersByFloor - encounteredEvents.length;
|
||||||
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * 5;
|
const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER;
|
||||||
|
|
||||||
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
|
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
|
||||||
|
|
||||||
if (roll < successRate) {
|
// 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;
|
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
||||||
// Reset base spawn weight
|
// Reset base spawn weight
|
||||||
this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
@ -1240,7 +1242,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
|
const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
|
||||||
const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
|
const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
|
||||||
const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
|
const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
|
||||||
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
|
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
|
||||||
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
|
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
|
||||||
this.trySpreadPokerus();
|
this.trySpreadPokerus();
|
||||||
if (!isNewBiome && (newWaveIndex % 10) === 5) {
|
if (!isNewBiome && (newWaveIndex % 10) === 5) {
|
||||||
@ -1248,14 +1250,19 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
if (resetArenaState) {
|
if (resetArenaState) {
|
||||||
this.arena.resetArenaEffects();
|
this.arena.resetArenaEffects();
|
||||||
if (lastBattle?.mysteryEncounter?.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
|
||||||
playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p)));
|
playerField.forEach((pokemon, p) => {
|
||||||
|
if (pokemon.isOnField()) {
|
||||||
|
this.pushPhase(new ReturnPhase(this, p));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (const pokemon of this.getParty()) {
|
for (const pokemon of this.getParty()) {
|
||||||
pokemon.resetBattleData();
|
pokemon.resetBattleData();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.trainer.visible) {
|
||||||
this.pushPhase(new ShowTrainerPhase(this));
|
this.pushPhase(new ShowTrainerPhase(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2960,8 +2967,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let availableEncounters: MysteryEncounter[] = [];
|
let availableEncounters: MysteryEncounter[] = [];
|
||||||
// New encounter will never be the same as the most recent encounter
|
// New encounter should never be the same as the most recent encounter
|
||||||
const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null;
|
const previousEncounter = this.mysteryEncounterData.encounteredEvents.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1].type : null;
|
||||||
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
|
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
|
||||||
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
|
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
|
||||||
while (availableEncounters.length === 0 && tier !== null) {
|
while (availableEncounters.length === 0 && tier !== null) {
|
||||||
@ -2977,10 +2984,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (!encounterCandidate.meetsRequirements!(this)) { // Meets encounter requirements
|
if (!encounterCandidate.meetsRequirements!(this)) { // Meets encounter requirements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isNullOrUndefined(previousEncounter) && encounterType === previousEncounter) { // Previous encounter was not this one
|
if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.mysteryEncounterData.encounteredEvents?.length > 0 && // Encounter has not exceeded max allowed encounters
|
if (this.mysteryEncounterData.encounteredEvents.length > 0 && // Encounter has not exceeded max allowed encounters
|
||||||
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
|
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
|
||||||
&& this.mysteryEncounterData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
|
&& this.mysteryEncounterData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -194,7 +194,11 @@ export default class Battle {
|
|||||||
|
|
||||||
getBgmOverride(scene: BattleScene): string | null {
|
getBgmOverride(scene: BattleScene): string | null {
|
||||||
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
||||||
if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
if (this.battleType === BattleType.MYSTERY_ENCOUNTER && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
|
||||||
|
// Music is overridden MEs during ME onInit()
|
||||||
|
// Should not use BGM overrides before swapping from DEFAULT mode
|
||||||
|
return null;
|
||||||
|
} else if (this.battleType === BattleType.TRAINER || this.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
||||||
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
||||||
}
|
}
|
||||||
|
@ -891,7 +891,7 @@ export abstract class BattleAnim {
|
|||||||
const isUser = frame.target === AnimFrameTarget.USER;
|
const isUser = frame.target === AnimFrameTarget.USER;
|
||||||
if (isUser && target === user) {
|
if (isUser && target === user) {
|
||||||
continue;
|
continue;
|
||||||
} else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET) {
|
} else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET && !target.isOnField()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
|
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
|
||||||
|
@ -294,7 +294,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
// Play animations
|
// Play animations
|
||||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||||
background.playWithoutTargets(scene, 230, 40, 2);
|
background.playWithoutTargets(scene, 230, 40, 2);
|
||||||
await transitionMysteryEncounterIntroVisuals(scene);
|
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -358,7 +358,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
// Play animations
|
// Play animations
|
||||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||||
background.playWithoutTargets(scene, 230, 40, 2);
|
background.playWithoutTargets(scene, 230, 40, 2);
|
||||||
await transitionMysteryEncounterIntroVisuals(scene);
|
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
@ -86,8 +86,8 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
|||||||
: ModifierTier.GREAT;
|
: ModifierTier.GREAT;
|
||||||
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
||||||
let item: ModifierTypeOption | null = null;
|
let item: ModifierTypeOption | null = null;
|
||||||
// TMs excluded from possible rewards as they're too swingy in value for a singular item reward
|
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
|
||||||
while (!item || item.type.id.includes("TM_")) {
|
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
|
||||||
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
|
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
|
||||||
}
|
}
|
||||||
encounter.setDialogueToken("itemName", item.type.name);
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
|
@ -32,7 +32,7 @@ const BST_INCREASE_VALUE = 10;
|
|||||||
*/
|
*/
|
||||||
export const TheStrongStuffEncounter: MysteryEncounter =
|
export const TheStrongStuffEncounter: MysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
|
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
|
||||||
.withHideWildIntroMessage(true)
|
.withHideWildIntroMessage(true)
|
||||||
@ -43,7 +43,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||||||
fileRoot: "items",
|
fileRoot: "items",
|
||||||
hasShadow: true,
|
hasShadow: true,
|
||||||
isItem: true,
|
isItem: true,
|
||||||
scale: 1.5,
|
scale: 1.25,
|
||||||
x: -15,
|
x: -15,
|
||||||
y: 3,
|
y: 3,
|
||||||
disableAnimation: true
|
disableAnimation: true
|
||||||
@ -53,7 +53,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||||||
fileRoot: "pokemon",
|
fileRoot: "pokemon",
|
||||||
hasShadow: true,
|
hasShadow: true,
|
||||||
repeat: true,
|
repeat: true,
|
||||||
scale: 1.5,
|
scale: 1.25,
|
||||||
x: 20,
|
x: 20,
|
||||||
y: 10,
|
y: 10,
|
||||||
yShadow: 7
|
yShadow: 7
|
||||||
@ -76,7 +76,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||||||
species: getPokemonSpecies(Species.SHUCKLE),
|
species: getPokemonSpecies(Species.SHUCKLE),
|
||||||
isBoss: true,
|
isBoss: true,
|
||||||
bossSegments: 5,
|
bossSegments: 5,
|
||||||
mysteryEncounterData: new MysteryEncounterPokemonData(1.5),
|
mysteryEncounterData: new MysteryEncounterPokemonData(1.25),
|
||||||
nature: Nature.BOLD,
|
nature: Nature.BOLD,
|
||||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||||
modifierConfigs: [
|
modifierConfigs: [
|
||||||
@ -142,7 +142,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||||||
if (index < 2) {
|
if (index < 2) {
|
||||||
// -15 to the two highest BST mons
|
// -15 to the two highest BST mons
|
||||||
modifyPlayerPokemonBST(pokemon, -HIGH_BST_REDUCTION_VALUE);
|
modifyPlayerPokemonBST(pokemon, -HIGH_BST_REDUCTION_VALUE);
|
||||||
encounter.setDialogueToken("highBstPokemon" + index, pokemon.getNameToRender());
|
encounter.setDialogueToken("highBstPokemon" + (index + 1), pokemon.getNameToRender());
|
||||||
} else {
|
} else {
|
||||||
// +10 for the rest
|
// +10 for the rest
|
||||||
modifyPlayerPokemonBST(pokemon, BST_INCREASE_VALUE);
|
modifyPlayerPokemonBST(pokemon, BST_INCREASE_VALUE);
|
||||||
|
@ -20,9 +20,9 @@ export class MysteryEncounterData {
|
|||||||
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
nextEncounterQueue: [MysteryEncounterType, integer][] = [];
|
nextEncounterQueue: [MysteryEncounterType, integer][] = [];
|
||||||
|
|
||||||
constructor(flags: MysteryEncounterData | null) {
|
constructor(data: MysteryEncounterData | null) {
|
||||||
if (!isNullOrUndefined(flags)) {
|
if (!isNullOrUndefined(data)) {
|
||||||
Object.assign(this, flags);
|
Object.assign(this, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,36 @@ import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounter
|
|||||||
import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter";
|
import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter";
|
||||||
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
||||||
|
|
||||||
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
/**
|
||||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
||||||
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 5;
|
*/
|
||||||
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15;
|
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
|
||||||
|
/**
|
||||||
|
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
|
||||||
|
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
||||||
|
*/
|
||||||
|
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3;
|
||||||
|
/**
|
||||||
|
* Specifies the target average for total ME spawns in a single Classic run.
|
||||||
|
* Used by anti-variance mechanic to check whether a run is above or below the target on a given wave.
|
||||||
|
*/
|
||||||
|
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
|
||||||
|
/**
|
||||||
|
* Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
||||||
|
* Example:
|
||||||
|
* AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors)
|
||||||
|
* ANTI_VARIANCE_WEIGHT_MODIFIER = 15
|
||||||
|
*
|
||||||
|
* On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs.
|
||||||
|
* So anti-variance adds 0/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME.
|
||||||
|
* So anti-variance adds 15/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME.
|
||||||
|
* So anti-variance adds -15/256 to the spawn weight check for ME spawn.
|
||||||
|
*/
|
||||||
|
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
|
||||||
|
|
||||||
export const EXTREME_ENCOUNTER_BIOMES = [
|
export const EXTREME_ENCOUNTER_BIOMES = [
|
||||||
Biome.SEA,
|
Biome.SEA,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Battle, { BattlerIndex, BattleType } from "#app/battle";
|
import Battle, { BattlerIndex, BattleType } from "#app/battle";
|
||||||
import { biomeLinks, BiomePoolTier } from "#app/data/biomes";
|
import { biomeLinks, BiomePoolTier } from "#app/data/biomes";
|
||||||
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon";
|
||||||
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
||||||
@ -908,13 +908,13 @@ export function handleMysteryEncounterTurnStartEffects(scene: BattleScene): bool
|
|||||||
export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) {
|
export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) {
|
||||||
const numRuns = 1000;
|
const numRuns = 1000;
|
||||||
let run = 0;
|
let run = 0;
|
||||||
const targetEncountersPerRun = 15; // AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
|
||||||
const biomes = Object.keys(Biome).filter(key => isNaN(Number(key)));
|
const biomes = Object.keys(Biome).filter(key => isNaN(Number(key)));
|
||||||
const alwaysPickTheseBiomes = [Biome.ISLAND, Biome.ABYSS, Biome.WASTELAND, Biome.FAIRY_CAVE, Biome.TEMPLE, Biome.LABORATORY, Biome.SPACE, Biome.WASTELAND];
|
const alwaysPickTheseBiomes = [Biome.ISLAND, Biome.ABYSS, Biome.WASTELAND, Biome.FAIRY_CAVE, Biome.TEMPLE, Biome.LABORATORY, Biome.SPACE, Biome.WASTELAND];
|
||||||
|
|
||||||
const calculateNumEncounters = (): any[] => {
|
const calculateNumEncounters = (): any[] => {
|
||||||
let encounterRate = baseSpawnWeight; // BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
let encounterRate = baseSpawnWeight; // BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
||||||
const numEncounters = [0, 0, 0, 0];
|
const numEncounters = [0, 0, 0, 0];
|
||||||
|
let mostRecentEncounterWave = 0;
|
||||||
const encountersByBiome = new Map<string, number>(biomes.map(b => [b, 0]));
|
const encountersByBiome = new Map<string, number>(biomes.map(b => [b, 0]));
|
||||||
const validMEfloorsByBiome = new Map<string, number>(biomes.map(b => [b, 0]));
|
const validMEfloorsByBiome = new Map<string, number>(biomes.map(b => [b, 0]));
|
||||||
let currentBiome = Biome.TOWN;
|
let currentBiome = Biome.TOWN;
|
||||||
@ -976,16 +976,20 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n
|
|||||||
|
|
||||||
// If total number of encounters is lower than expected for the run, slightly favor a new encounter
|
// If total number of encounters is lower than expected for the run, slightly favor a new encounter
|
||||||
// Do the reverse as well
|
// Do the reverse as well
|
||||||
const expectedEncountersByFloor = targetEncountersPerRun / (180 - 10) * i;
|
const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (180 - 10) * (i - 10);
|
||||||
const currentRunDiffFromAvg = expectedEncountersByFloor - numEncounters.reduce((a, b) => a + b);
|
const currentRunDiffFromAvg = expectedEncountersByFloor - numEncounters.reduce((a, b) => a + b);
|
||||||
const favoredEncounterRate = encounterRate + currentRunDiffFromAvg * 5;
|
const favoredEncounterRate = encounterRate + currentRunDiffFromAvg * 15;
|
||||||
|
|
||||||
if (roll < favoredEncounterRate) {
|
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
|
||||||
|
const canSpawn = (i - mostRecentEncounterWave) > 3;
|
||||||
|
|
||||||
|
if (canSpawn && roll < favoredEncounterRate) {
|
||||||
|
mostRecentEncounterWave = i;
|
||||||
encounterRate = baseSpawnWeight;
|
encounterRate = baseSpawnWeight;
|
||||||
|
|
||||||
// Calculate encounter rarity
|
// Calculate encounter rarity
|
||||||
// Common / Uncommon / Rare / Super Rare (base is out of 128)
|
// Common / Uncommon / Rare / Super Rare (base is out of 128)
|
||||||
const tierWeights = [64, 40, 21, 3];
|
const tierWeights = [66, 40, 19, 3];
|
||||||
|
|
||||||
// Adjust tier weights by currently encountered events (pity system that lowers odds of multiple common/uncommons)
|
// Adjust tier weights by currently encountered events (pity system that lowers odds of multiple common/uncommons)
|
||||||
tierWeights[0] = tierWeights[0] - 6 * numEncounters[0];
|
tierWeights[0] = tierWeights[0] - 6 * numEncounters[0];
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
export enum MysteryEncounterMode {
|
export enum MysteryEncounterMode {
|
||||||
|
/** MysteryEncounter will always begin in this mode, but will always swap modes when an option is selected */
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
TRAINER_BATTLE,
|
TRAINER_BATTLE,
|
||||||
WILD_BATTLE,
|
WILD_BATTLE,
|
||||||
/** Enables wild boss music during encounter */
|
/** Enables special boss music during encounter */
|
||||||
BOSS_BATTLE,
|
BOSS_BATTLE,
|
||||||
NO_BATTLE
|
NO_BATTLE
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Enum values are base spawn weights of each tier
|
* Enum values are base spawn weights of each tier.
|
||||||
|
* The weights aim for 46.25/31.25/18.5/4% spawn ratios, AFTER accounting for anti-variance and pity mechanisms
|
||||||
*/
|
*/
|
||||||
export enum MysteryEncounterTier {
|
export enum MysteryEncounterTier {
|
||||||
COMMON = 64,
|
COMMON = 66,
|
||||||
GREAT = 40,
|
GREAT = 40,
|
||||||
ULTRA = 21,
|
ULTRA = 19,
|
||||||
ROGUE = 3,
|
ROGUE = 3,
|
||||||
MASTER = 0 // Not currently used
|
MASTER = 0 // Not currently used
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { variantData } from "#app/data/variant";
|
|||||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
||||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
|
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
|
||||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||||
import { Constructor } from "#app/utils";
|
import { Constructor, isNullOrUndefined } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
||||||
import { getLevelTotalExp } from "../data/exp";
|
import { getLevelTotalExp } from "../data/exp";
|
||||||
@ -577,7 +577,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const formKey = this.getFormKey();
|
const formKey = this.getFormKey();
|
||||||
if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) {
|
if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) {
|
||||||
return 1.5;
|
return 1.5;
|
||||||
} else if (this?.mysteryEncounterData?.spriteScale) {
|
} else if (!isNullOrUndefined(this.mysteryEncounterData?.spriteScale)) {
|
||||||
return this.mysteryEncounterData.spriteScale;
|
return this.mysteryEncounterData.spriteScale;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
"query": "What will you do?",
|
"query": "What will you do?",
|
||||||
"option": {
|
"option": {
|
||||||
"1": {
|
"1": {
|
||||||
"label": "Touch the Shuckle",
|
"label": "Approach the Shuckle",
|
||||||
"tooltip": "(?) Something awful or amazing might happen",
|
"tooltip": "(?) Something awful or amazing might happen",
|
||||||
"selected": "You black out.",
|
"selected": "You black out.",
|
||||||
"selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}} feel a terrible lethargy come over them!\nTheir base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!"
|
"selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!"
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"label": "Battle the Shuckle",
|
"label": "Battle the Shuckle",
|
||||||
|
@ -141,9 +141,9 @@ class DefaultOverrides {
|
|||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
/** 1 to 256, set to null to ignore */
|
/** 1 to 256, set to null to ignore */
|
||||||
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = null;
|
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = 256;
|
||||||
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null;
|
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null;
|
||||||
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = null;
|
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = MysteryEncounterType.CLOWNING_AROUND;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// MODIFIER / ITEM OVERRIDES
|
// MODIFIER / ITEM OVERRIDES
|
||||||
|
@ -6,6 +6,7 @@ import { StatusEffect } from "#app/enums/status-effect.js";
|
|||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
|
import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { BattleType } from "#app/battle";
|
||||||
|
|
||||||
export class PostSummonPhase extends PokemonPhase {
|
export class PostSummonPhase extends PokemonPhase {
|
||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex) {
|
constructor(scene: BattleScene, battlerIndex: BattlerIndex) {
|
||||||
@ -23,7 +24,7 @@ export class PostSummonPhase extends PokemonPhase {
|
|||||||
this.scene.arena.applyTags(ArenaTrapTag, pokemon);
|
this.scene.arena.applyTags(ArenaTrapTag, pokemon);
|
||||||
|
|
||||||
// If this is mystery encounter and has post summon phase tag, apply post summon effects
|
// If this is mystery encounter and has post summon phase tag, apply post summon effects
|
||||||
if (pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag)) {
|
if (this.scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) {
|
||||||
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,14 @@ import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-d
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
|
||||||
|
|
||||||
export default class MysteryEncounterUiHandler extends UiHandler {
|
export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
private cursorContainer: Phaser.GameObjects.Container;
|
private cursorContainer: Phaser.GameObjects.Container;
|
||||||
private cursorObj?: Phaser.GameObjects.Image;
|
private cursorObj?: Phaser.GameObjects.Image;
|
||||||
|
|
||||||
private optionsContainer: Phaser.GameObjects.Container;
|
private optionsContainer: Phaser.GameObjects.Container;
|
||||||
|
private optionScrollTweens: (Phaser.Tweens.Tween | null)[] = [null, null, null, null];
|
||||||
|
|
||||||
private tooltipWindow: Phaser.GameObjects.NineSlice;
|
private tooltipWindow: Phaser.GameObjects.NineSlice;
|
||||||
private tooltipContainer: Phaser.GameObjects.Container;
|
private tooltipContainer: Phaser.GameObjects.Container;
|
||||||
@ -341,16 +343,17 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||||||
for (let i = 0; i < this.encounterOptions.length; i++) {
|
for (let i = 0; i < this.encounterOptions.length; i++) {
|
||||||
const option = this.encounterOptions[i];
|
const option = this.encounterOptions[i];
|
||||||
|
|
||||||
let optionText;
|
let optionText: BBCodeText;
|
||||||
switch (this.encounterOptions.length) {
|
switch (this.encounterOptions.length) {
|
||||||
|
default:
|
||||||
case 2:
|
case 2:
|
||||||
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, 8, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 });
|
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, 8, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 });
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 });
|
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 });
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 });
|
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { fontSize: "80px", lineSpacing: -8 });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,6 +378,38 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||||||
if (this.blockInput) {
|
if (this.blockInput) {
|
||||||
optionText.setAlpha(0.5);
|
optionText.setAlpha(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets up the mask that hides the option text to give an illusion of scrolling
|
||||||
|
const nonScrollWidth = 90;
|
||||||
|
const optionTextMaskRect = this.scene.make.graphics({});
|
||||||
|
optionTextMaskRect.setScale(6);
|
||||||
|
optionTextMaskRect.fillStyle(0xFFFFFF);
|
||||||
|
optionTextMaskRect.beginPath();
|
||||||
|
optionTextMaskRect.fillRect(optionText.x + 11, optionText.y + 140, nonScrollWidth, 18);
|
||||||
|
|
||||||
|
const optionTextMask = optionTextMaskRect.createGeometryMask();
|
||||||
|
optionText.setMask(optionTextMask);
|
||||||
|
|
||||||
|
const optionTextWidth = optionText.displayWidth;
|
||||||
|
|
||||||
|
const tween = this.optionScrollTweens[i];
|
||||||
|
if (tween) {
|
||||||
|
tween.remove();
|
||||||
|
this.optionScrollTweens[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animates the option text scrolling sideways
|
||||||
|
if (optionTextWidth > nonScrollWidth) {
|
||||||
|
this.optionScrollTweens[i] = this.scene.tweens.add({
|
||||||
|
targets: optionText,
|
||||||
|
delay: Utils.fixedInt(2000),
|
||||||
|
loop: -1,
|
||||||
|
hold: Utils.fixedInt(2000),
|
||||||
|
duration: Utils.fixedInt((optionTextWidth - nonScrollWidth) / 15 * 2000),
|
||||||
|
x: `-=${(optionTextWidth - nonScrollWidth)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.optionsContainer.add(optionText);
|
this.optionsContainer.add(optionText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user