Even more array improvements

This commit is contained in:
Sirz Benjie 2025-09-22 18:42:50 -05:00
parent 8003215c39
commit ac2f09ad33
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
15 changed files with 178 additions and 169 deletions

View File

@ -7,8 +7,8 @@ import type { TrainerPartyTemplate } from "#trainers/trainer-party-template";
export type PartyTemplateFunc = () => TrainerPartyTemplate; export type PartyTemplateFunc = () => TrainerPartyTemplate;
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon; export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
export type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[]; export type GenModifiersFunc = (party: readonly EnemyPokemon[]) => PersistentModifier[];
export type GenAIFunc = (party: EnemyPokemon[]) => void; export type GenAIFunc = (party: readonly EnemyPokemon[]) => void;
export interface TrainerTierPools { export interface TrainerTierPools {
[key: number]: SpeciesId[]; [key: number]: SpeciesId[];

View File

@ -526,24 +526,25 @@ export class FixedBattleConfig {
/** /**
* Helper function to generate a random trainer for evil team trainers and the elite 4/champion * Helper function to generate a random trainer for evil team trainers and the elite 4/champion
* @param trainerPool The TrainerType or list of TrainerTypes that can possibly be generated * @param trainerPool - An array of trainer types to choose from. If an entry is an array, a random trainer type will be chosen from that array
* @param randomGender whether or not to randomly (50%) generate a female trainer (for use with evil team grunts) * @param randomGender - Whether or not to randomly (50%) generate a female trainer (for use with evil team grunts)
* @param seedOffset the seed offset to use for the random generation of the trainer * @param seedOffset - The seed offset to use for the random generation of the trainer
* @returns the generated trainer * @returns A function to get a random trainer
*/ */
export function getRandomTrainerFunc( export function getRandomTrainerFunc(
trainerPool: (TrainerType | TrainerType[])[], trainerPool: readonly (TrainerType | readonly TrainerType[])[],
randomGender = false, randomGender = false,
seedOffset = 0, seedOffset = 0,
): GetTrainerFunc { ): GetTrainerFunc {
return () => { return () => {
const rand = randSeedInt(trainerPool.length); const rand = randSeedInt(trainerPool.length);
const trainerTypes: TrainerType[] = []; const trainerTypes: TrainerType[] = new Array(trainerPool.length);
globalScene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) { for (let i = 0; i < trainerPool.length; i++) {
const trainerPoolEntry = trainerPool[i];
const trainerType = Array.isArray(trainerPoolEntry) ? randSeedItem(trainerPoolEntry) : trainerPoolEntry; const trainerType = Array.isArray(trainerPoolEntry) ? randSeedItem(trainerPoolEntry) : trainerPoolEntry;
trainerTypes.push(trainerType); trainerTypes[i] = trainerType;
} }
}, seedOffset); }, seedOffset);

View File

@ -23,7 +23,7 @@ export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
/** /**
* The default species that a new player can choose from * The default species that a new player can choose from
*/ */
export const defaultStarterSpecies: SpeciesId[] = [ export const defaultStarterSpecies: readonly SpeciesId[] = [
SpeciesId.BULBASAUR, SpeciesId.BULBASAUR,
SpeciesId.CHARMANDER, SpeciesId.CHARMANDER,
SpeciesId.SQUIRTLE, SpeciesId.SQUIRTLE,

View File

@ -5,6 +5,7 @@ import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import type { Mutable } from "#types/type-helpers";
import { randSeedInt } from "#utils/common"; import { randSeedInt } from "#utils/common";
import { getEnumValues } from "#utils/enums"; import { getEnumValues } from "#utils/enums";
import { toCamelCase } from "#utils/strings"; import { toCamelCase } from "#utils/strings";
@ -88,19 +89,19 @@ export enum BiomePoolTier {
export const uncatchableSpecies: SpeciesId[] = []; export const uncatchableSpecies: SpeciesId[] = [];
interface SpeciesTree { interface SpeciesTree {
[key: number]: SpeciesId[] readonly [key: number]: SpeciesId[]
} }
export interface PokemonPools { export interface PokemonPools {
[key: number]: (SpeciesId | SpeciesTree)[] readonly [key: number]: (SpeciesId | SpeciesTree)[]
} }
interface BiomeTierPokemonPools { interface BiomeTierPokemonPools {
[key: number]: PokemonPools readonly [key: number]: PokemonPools
} }
interface BiomePokemonPools { interface BiomePokemonPools {
[key: number]: BiomeTierPokemonPools readonly [key: number]: BiomeTierPokemonPools
} }
export interface BiomeTierTod { export interface BiomeTierTod {
@ -110,17 +111,17 @@ export interface BiomeTierTod {
} }
export interface CatchableSpecies{ export interface CatchableSpecies{
[key: number]: BiomeTierTod[] readonly [key: number]: readonly BiomeTierTod[]
} }
export const catchableSpecies: CatchableSpecies = {}; export const catchableSpecies: CatchableSpecies = {};
export interface BiomeTierTrainerPools { export interface BiomeTierTrainerPools {
[key: number]: TrainerType[] readonly [key: number]: readonly TrainerType[]
} }
export interface BiomeTrainerPools { export interface BiomeTrainerPools {
[key: number]: BiomeTierTrainerPools readonly [key: number]: BiomeTierTrainerPools
} }
export const biomePokemonPools: BiomePokemonPools = { export const biomePokemonPools: BiomePokemonPools = {
@ -7621,12 +7622,10 @@ export function initBiomes() {
? biomeLinks[biome] as (BiomeId | [ BiomeId, number ])[] ? biomeLinks[biome] as (BiomeId | [ BiomeId, number ])[]
: [ biomeLinks[biome] as BiomeId ]; : [ biomeLinks[biome] as BiomeId ];
for (const linkedBiomeEntry of linkedBiomes) { for (const linkedBiomeEntry of linkedBiomes) {
const linkedBiome = !Array.isArray(linkedBiomeEntry) const linkedBiome = Array.isArray(linkedBiomeEntry)
? linkedBiomeEntry as BiomeId ? linkedBiomeEntry[0] : linkedBiomeEntry as BiomeId;
: linkedBiomeEntry[0]; const biomeChance = Array.isArray(linkedBiomeEntry)
const biomeChance = !Array.isArray(linkedBiomeEntry) ? linkedBiomeEntry[1] : 1;
? 1
: linkedBiomeEntry[1];
if (!biomeDepths.hasOwnProperty(linkedBiome) || biomeChance < biomeDepths[linkedBiome][1] || (depth < biomeDepths[linkedBiome][0] && biomeChance === biomeDepths[linkedBiome][1])) { if (!biomeDepths.hasOwnProperty(linkedBiome) || biomeChance < biomeDepths[linkedBiome][1] || (depth < biomeDepths[linkedBiome][0] && biomeChance === biomeDepths[linkedBiome][1])) {
biomeDepths[linkedBiome] = [ depth + 1, biomeChance ]; biomeDepths[linkedBiome] = [ depth + 1, biomeChance ];
traverseBiome(linkedBiome, depth + 1); traverseBiome(linkedBiome, depth + 1);
@ -7638,15 +7637,15 @@ export function initBiomes() {
biomeDepths[BiomeId.END] = [ Object.values(biomeDepths).map(d => d[0]).reduce((max: number, value: number) => Math.max(max, value), 0) + 1, 1 ]; biomeDepths[BiomeId.END] = [ Object.values(biomeDepths).map(d => d[0]).reduce((max: number, value: number) => Math.max(max, value), 0) + 1, 1 ];
for (const biome of getEnumValues(BiomeId)) { for (const biome of getEnumValues(BiomeId)) {
biomePokemonPools[biome] = {}; (biomePokemonPools[biome] as Mutable<typeof biomePokemonPools[number]>) = {};
biomeTrainerPools[biome] = {}; (biomeTrainerPools[biome] as Mutable<typeof biomeTrainerPools[number]>) = {};
for (const tier of getEnumValues(BiomePoolTier)) { for (const tier of getEnumValues(BiomePoolTier)) {
biomePokemonPools[biome][tier] = {}; (biomePokemonPools[biome][tier] as Mutable<typeof biomePokemonPools[number][number]>) = {};
biomeTrainerPools[biome][tier] = []; (biomeTrainerPools[biome][tier] as Mutable<typeof biomeTrainerPools[number][number]>) = [];
for (const tod of getEnumValues(TimeOfDay)) { for (const tod of getEnumValues(TimeOfDay)) {
biomePokemonPools[biome][tier][tod] = []; (biomePokemonPools[biome][tier][tod] as Mutable<typeof biomePokemonPools[number][number][number]>) = [];
} }
} }
} }
@ -7663,8 +7662,9 @@ export function initBiomes() {
uncatchableSpecies.push(speciesId); uncatchableSpecies.push(speciesId);
} }
type mutableSpecies = Mutable<typeof catchableSpecies[SpeciesId]>;
// array of biome options for the current species // array of biome options for the current species
catchableSpecies[speciesId] = []; (catchableSpecies[speciesId] as mutableSpecies) = [];
for (const b of biomeEntries) { for (const b of biomeEntries) {
const biome = b[0]; const biome = b[0];
@ -7675,7 +7675,7 @@ export function initBiomes() {
: [ b[2] ] : [ b[2] ]
: [ TimeOfDay.ALL ]; : [ TimeOfDay.ALL ];
catchableSpecies[speciesId].push({ (catchableSpecies[speciesId] as mutableSpecies).push({
biome: biome as BiomeId, biome: biome as BiomeId,
tier: tier as BiomePoolTier, tier: tier as BiomePoolTier,
tod: timesOfDay as TimeOfDay[] tod: timesOfDay as TimeOfDay[]
@ -7735,12 +7735,13 @@ export function initBiomes() {
}; };
for (let s = 1; s < entry.length; s++) { for (let s = 1; s < entry.length; s++) {
const speciesId = entry[s]; const speciesId = entry[s];
// biome-ignore lint/nursery/noShadow: one-off
const prevolution = entry.flatMap((s: string | number) => pokemonEvolutions[s]).find(e => e && e.speciesId === speciesId); const prevolution = entry.flatMap((s: string | number) => pokemonEvolutions[s]).find(e => e && e.speciesId === speciesId);
const level = prevolution.level - (prevolution.level === 1 ? 1 : 0) + (prevolution.wildDelay * 10) - (tier >= BiomePoolTier.BOSS ? 10 : 0); const level = prevolution.level - (prevolution.level === 1 ? 1 : 0) + (prevolution.wildDelay * 10) - (tier >= BiomePoolTier.BOSS ? 10 : 0);
if (!newEntry.hasOwnProperty(level)) { if (newEntry.hasOwnProperty(level)) {
newEntry[level] = [ speciesId ];
} else {
newEntry[level].push(speciesId); newEntry[level].push(speciesId);
} else {
newEntry[level] = [ speciesId ];
} }
} }
biomeTierTimePool[e] = newEntry; biomeTierTimePool[e] = newEntry;
@ -7763,7 +7764,7 @@ export function initBiomes() {
} }
const biomeTierPool = biomeTrainerPools[biome][tier]; const biomeTierPool = biomeTrainerPools[biome][tier];
biomeTierPool.push(trainerType); (biomeTierPool as Mutable<typeof biomeTierPool>).push(trainerType);
} }
//outputPools(); //outputPools();
} }

View File

@ -2154,8 +2154,8 @@ export class HighestStatBoostTag extends AbilityBattlerTag {
} }
export class WeatherHighestStatBoostTag extends HighestStatBoostTag { export class WeatherHighestStatBoostTag extends HighestStatBoostTag {
#weatherTypes: WeatherType[]; readonly #weatherTypes: readonly WeatherType[];
public get weatherTypes(): WeatherType[] { public get weatherTypes(): readonly WeatherType[] {
return this.#weatherTypes; return this.#weatherTypes;
} }
@ -2166,8 +2166,8 @@ export class WeatherHighestStatBoostTag extends HighestStatBoostTag {
} }
export class TerrainHighestStatBoostTag extends HighestStatBoostTag { export class TerrainHighestStatBoostTag extends HighestStatBoostTag {
#terrainTypes: TerrainType[]; readonly #terrainTypes: readonly TerrainType[];
public get terrainTypes(): TerrainType[] { public get terrainTypes(): readonly TerrainType[] {
return this.#terrainTypes; return this.#terrainTypes;
} }

View File

@ -485,14 +485,14 @@ export class Egg {
* and being the same each time * and being the same each time
*/ */
let totalWeight = 0; let totalWeight = 0;
const speciesWeights: number[] = []; const speciesWeights = new Array<number>(speciesPool.length);
for (const speciesId of speciesPool) { for (const [idx, speciesId] of speciesPool.entries()) {
// Accounts for species that have starter costs outside of the normal range for their EggTier // Accounts for species that have starter costs outside of the normal range for their EggTier
const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue); const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue);
const weight = Math.floor( const weight = Math.floor(
(((maxStarterValue - speciesCostClamped) / (maxStarterValue - minStarterValue + 1)) * 1.5 + 1) * 100, (((maxStarterValue - speciesCostClamped) / (maxStarterValue - minStarterValue + 1)) * 1.5 + 1) * 100,
); );
speciesWeights.push(totalWeight + weight); speciesWeights[idx] = totalWeight + weight;
totalWeight += weight; totalWeight += weight;
} }

View File

@ -25,6 +25,7 @@ import { SpeciesId } from "#enums/species-id";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import type { Constructor, nil } from "#types/common"; import type { Constructor, nil } from "#types/common";
import type { Mutable } from "#types/type-helpers";
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void; export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
@ -116,7 +117,7 @@ function getSpeciesDependentFormChangeCondition(species: SpeciesId): SpeciesForm
} }
interface PokemonFormChanges { interface PokemonFormChanges {
[key: string]: SpeciesFormChange[]; [key: string]: readonly SpeciesFormChange[];
} }
// biome-ignore format: manually formatted // biome-ignore format: manually formatted
@ -608,6 +609,6 @@ export function initPokemonForms() {
); );
} }
} }
formChanges.push(...newFormChanges); (formChanges as Mutable<typeof formChanges>).push(...newFormChanges);
} }
} }

View File

@ -240,9 +240,9 @@ export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
/** The ability that triggers the form change */ /** The ability that triggers the form change */
public ability: AbilityId; public ability: AbilityId;
/** The list of weathers that trigger the form change */ /** The list of weathers that trigger the form change */
public weathers: WeatherType[]; public readonly weathers: readonly WeatherType[];
constructor(ability: AbilityId, weathers: WeatherType[]) { constructor(ability: AbilityId, weathers: readonly WeatherType[]) {
super(); super();
this.ability = ability; this.ability = ability;
this.weathers = weathers; this.weathers = weathers;
@ -278,9 +278,9 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
/** The ability that triggers the form change*/ /** The ability that triggers the form change*/
public ability: AbilityId; public ability: AbilityId;
/** The list of weathers that will also trigger a form change to original form */ /** The list of weathers that will also trigger a form change to original form */
public weathers: WeatherType[]; public readonly weathers: readonly WeatherType[];
constructor(ability: AbilityId, weathers: WeatherType[]) { constructor(ability: AbilityId, weathers: readonly WeatherType[]) {
super(); super();
this.ability = ability; this.ability = ability;
this.weathers = weathers; this.weathers = weathers;
@ -310,9 +310,10 @@ export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChange
} }
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string { export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1; const formKey = formChange.formKey;
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1; const isMega = formKey.indexOf(SpeciesFormKey.MEGA) > -1;
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1; const isGmax = formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
const isEmax = formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey; const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
if (isMega) { if (isMega) {
return i18next.t("battlePokemonForm:megaChange", { return i18next.t("battlePokemonForm:megaChange", {

View File

@ -2140,7 +2140,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Suppresses an ability and calls its onlose attributes * Suppresses an ability and calls its onlose attributes
*/ */
public suppressAbility() { public suppressAbility() {
[true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: this, passive })); applyOnLoseAbAttrs({ pokemon: this, passive: true });
applyOnLoseAbAttrs({ pokemon: this, passive: false });
this.summonData.abilitySuppressed = true; this.summonData.abilitySuppressed = true;
} }

View File

@ -642,14 +642,14 @@ export class Trainer extends Phaser.GameObjects.Container {
} }
} }
genModifiers(party: EnemyPokemon[]): PersistentModifier[] { genModifiers(party: readonly EnemyPokemon[]): PersistentModifier[] {
if (this.config.genModifiersFunc) { if (this.config.genModifiersFunc) {
return this.config.genModifiersFunc(party); return this.config.genModifiersFunc(party);
} }
return []; return [];
} }
genAI(party: EnemyPokemon[]) { genAI(party: readonly EnemyPokemon[]) {
if (this.config.genAIFuncs) { if (this.config.genAIFuncs) {
this.config.genAIFuncs.forEach(f => f(party)); this.config.genAIFuncs.forEach(f => f(party));
} }

View File

@ -277,7 +277,7 @@ export class ModifierType {
} }
} }
type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType | null; type ModifierTypeGeneratorFunc = (party: readonly Pokemon[], pregenArgs?: any[]) => ModifierType | null;
export class ModifierTypeGenerator extends ModifierType { export class ModifierTypeGenerator extends ModifierType {
private genTypeFunc: ModifierTypeGeneratorFunc; private genTypeFunc: ModifierTypeGeneratorFunc;
@ -287,7 +287,7 @@ export class ModifierTypeGenerator extends ModifierType {
this.genTypeFunc = genTypeFunc; this.genTypeFunc = genTypeFunc;
} }
generateType(party: Pokemon[], pregenArgs?: any[]) { generateType(party: readonly Pokemon[], pregenArgs?: any[]) {
const ret = this.genTypeFunc(party, pregenArgs); const ret = this.genTypeFunc(party, pregenArgs);
if (ret) { if (ret) {
ret.id = this.id; ret.id = this.id;
@ -2360,7 +2360,11 @@ const tierWeights = [768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024];
*/ */
export const itemPoolChecks: Map<ModifierTypeKeys, boolean | undefined> = new Map(); export const itemPoolChecks: Map<ModifierTypeKeys, boolean | undefined> = new Map();
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount = 0) { export function regenerateModifierPoolThresholds(
party: readonly Pokemon[],
poolType: ModifierPoolType,
rerollCount = 0,
) {
const pool = getModifierPoolForType(poolType); const pool = getModifierPoolForType(poolType);
itemPoolChecks.forEach((_v, k) => { itemPoolChecks.forEach((_v, k) => {
itemPoolChecks.set(k, false); itemPoolChecks.set(k, false);
@ -2906,7 +2910,7 @@ export class ModifierTypeOption {
* @param party The player's party. * @param party The player's party.
* @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode. * @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode.
*/ */
export function getPartyLuckValue(party: Pokemon[]): number { export function getPartyLuckValue(party: readonly Pokemon[]): number {
if (globalScene.gameMode.isDaily) { if (globalScene.gameMode.isDaily) {
const DailyLuck = new NumberHolder(0); const DailyLuck = new NumberHolder(0);
globalScene.executeWithSeedOffset( globalScene.executeWithSeedOffset(

View File

@ -12,7 +12,7 @@ export abstract class BattlePhase extends Phase {
const tintSprites = globalScene.currentBattle.trainer.getTintSprites(); const tintSprites = globalScene.currentBattle.trainer.getTintSprites();
for (let i = 0; i < sprites.length; i++) { for (let i = 0; i < sprites.length; i++) {
const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2;
[sprites[i], tintSprites[i]].map(sprite => { [sprites[i], tintSprites[i]].forEach(sprite => {
if (visible) { if (visible) {
sprite.x = trainerSlot || sprites.length < 2 ? 0 : i ? 16 : -16; sprite.x = trainerSlot || sprites.length < 2 ? 0 : i ? 16 : -16;
} }

View File

@ -18,59 +18,59 @@ export enum EventType {
} }
interface EventBanner { interface EventBanner {
bannerKey?: string; readonly bannerKey?: string;
xOffset?: number; readonly xOffset?: number;
yOffset?: number; readonly yOffset?: number;
scale?: number; readonly scale?: number;
availableLangs?: string[]; readonly availableLangs?: readonly string[];
} }
interface EventEncounter { interface EventEncounter {
species: SpeciesId; readonly species: SpeciesId;
blockEvolution?: boolean; readonly blockEvolution?: boolean;
formIndex?: number; readonly formIndex?: number;
} }
interface EventMysteryEncounterTier { interface EventMysteryEncounterTier {
mysteryEncounter: MysteryEncounterType; readonly mysteryEncounter: MysteryEncounterType;
tier?: MysteryEncounterTier; readonly tier?: MysteryEncounterTier;
disable?: boolean; readonly disable?: boolean;
} }
interface EventWaveReward { interface EventWaveReward {
wave: number; readonly wave: number;
type: string; readonly type: string;
} }
type EventMusicReplacement = [string, string]; type EventMusicReplacement = readonly [string, string];
interface EventChallenge { interface EventChallenge {
challenge: Challenges; readonly challenge: Challenges;
value: number; readonly value: number;
} }
interface TimedEvent extends EventBanner { interface TimedEvent extends EventBanner {
name: string; readonly name: string;
eventType: EventType; readonly eventType: EventType;
shinyMultiplier?: number; readonly shinyMultiplier?: number;
classicFriendshipMultiplier?: number; readonly classicFriendshipMultiplier?: number;
luckBoost?: number; readonly luckBoost?: number;
upgradeUnlockedVouchers?: boolean; readonly upgradeUnlockedVouchers?: boolean;
startDate: Date; readonly startDate: Date;
endDate: Date; readonly endDate: Date;
eventEncounters?: EventEncounter[]; readonly eventEncounters?: readonly EventEncounter[];
delibirdyBuff?: string[]; readonly delibirdyBuff?: readonly string[];
weather?: WeatherPoolEntry[]; readonly weather?: readonly WeatherPoolEntry[];
mysteryEncounterTierChanges?: EventMysteryEncounterTier[]; readonly mysteryEncounterTierChanges?: readonly EventMysteryEncounterTier[];
luckBoostedSpecies?: SpeciesId[]; readonly luckBoostedSpecies?: readonly SpeciesId[];
boostFusions?: boolean; //MODIFIER REWORK PLEASE readonly boostFusions?: boolean; //MODIFIER REWORK PLEASE
classicWaveRewards?: EventWaveReward[]; // Rival battle rewards readonly classicWaveRewards?: readonly EventWaveReward[]; // Rival battle rewards
trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny readonly trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny
music?: EventMusicReplacement[]; readonly music?: readonly EventMusicReplacement[];
dailyRunChallenges?: EventChallenge[]; readonly dailyRunChallenges?: readonly EventChallenge[];
} }
const timedEvents: TimedEvent[] = [ const timedEvents: readonly TimedEvent[] = [
{ {
name: "Winter Holiday Update", name: "Winter Holiday Update",
eventType: EventType.SHINY, eventType: EventType.SHINY,
@ -385,7 +385,8 @@ const timedEvents: TimedEvent[] = [
export class TimedEventManager { export class TimedEventManager {
isActive(event: TimedEvent) { isActive(event: TimedEvent) {
return event.startDate < new Date() && new Date() < event.endDate; const now = new Date();
return event.startDate < now && now < event.endDate;
} }
activeEvent(): TimedEvent | undefined { activeEvent(): TimedEvent | undefined {
@ -427,19 +428,17 @@ export class TimedEventManager {
getEventBannerLangs(): string[] { getEventBannerLangs(): string[] {
const ret: string[] = []; const ret: string[] = [];
ret.push(...timedEvents.find(te => this.isActive(te) && te.availableLangs != null)?.availableLangs!); ret.push(...(timedEvents.find(te => this.isActive(te) && te.availableLangs != null)?.availableLangs ?? []));
return ret; return ret;
} }
getEventEncounters(): EventEncounter[] { getEventEncounters(): EventEncounter[] {
const ret: EventEncounter[] = []; const ret: EventEncounter[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te)) if (this.isActive(te) && te.eventEncounters != null) {
.map(te => { ret.push(...te.eventEncounters);
if (te.eventEncounters != null) { }
ret.push(...te.eventEncounters); }
}
});
return ret; return ret;
} }
@ -472,13 +471,11 @@ export class TimedEventManager {
*/ */
getDelibirdyBuff(): string[] { getDelibirdyBuff(): string[] {
const ret: string[] = []; const ret: string[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te)) if (this.isActive(te) && te.delibirdyBuff != null) {
.map(te => { ret.push(...te.delibirdyBuff);
if (te.delibirdyBuff != null) { }
ret.push(...te.delibirdyBuff); }
}
});
return ret; return ret;
} }
@ -488,39 +485,35 @@ export class TimedEventManager {
*/ */
getWeather(): WeatherPoolEntry[] { getWeather(): WeatherPoolEntry[] {
const ret: WeatherPoolEntry[] = []; const ret: WeatherPoolEntry[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te)) if (this.isActive(te) && te.weather != null) {
.map(te => { ret.push(...te.weather);
if (te.weather != null) { }
ret.push(...te.weather); }
}
});
return ret; return ret;
} }
getAllMysteryEncounterChanges(): EventMysteryEncounterTier[] { getAllMysteryEncounterChanges(): EventMysteryEncounterTier[] {
const ret: EventMysteryEncounterTier[] = []; const ret: EventMysteryEncounterTier[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te)) if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
.map(te => { ret.push(...te.mysteryEncounterTierChanges);
if (te.mysteryEncounterTierChanges != null) { }
ret.push(...te.mysteryEncounterTierChanges); }
}
});
return ret; return ret;
} }
getEventMysteryEncountersDisabled(): MysteryEncounterType[] { getEventMysteryEncountersDisabled(): MysteryEncounterType[] {
const ret: MysteryEncounterType[] = []; const ret: MysteryEncounterType[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te) && te.mysteryEncounterTierChanges != null) if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
.map(te => { for (const metc of te.mysteryEncounterTierChanges) {
te.mysteryEncounterTierChanges?.map(metc => {
if (metc.disable) { if (metc.disable) {
ret.push(metc.mysteryEncounter); ret.push(metc.mysteryEncounter);
} }
}); }
}); }
}
return ret; return ret;
} }
@ -529,15 +522,15 @@ export class TimedEventManager {
normal: MysteryEncounterTier, normal: MysteryEncounterTier,
): MysteryEncounterTier { ): MysteryEncounterTier {
let ret = normal; let ret = normal;
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te) && te.mysteryEncounterTierChanges != null) if (this.isActive(te) && te.mysteryEncounterTierChanges != null) {
.map(te => { for (const metc of te.mysteryEncounterTierChanges) {
te.mysteryEncounterTierChanges?.map(metc => {
if (metc.mysteryEncounter === encounterType) { if (metc.mysteryEncounter === encounterType) {
ret = metc.tier ?? normal; ret = metc.tier ?? normal;
} }
}); }
}); }
}
return ret; return ret;
} }
@ -551,15 +544,16 @@ export class TimedEventManager {
} }
getEventLuckBoostedSpecies(): SpeciesId[] { getEventLuckBoostedSpecies(): SpeciesId[] {
const ret: SpeciesId[] = []; const ret = new Set<SpeciesId>();
timedEvents
.filter(te => this.isActive(te)) for (const te of timedEvents) {
.map(te => { if (this.isActive(te) && te.luckBoostedSpecies != null) {
if (te.luckBoostedSpecies != null) { for (const s of te.luckBoostedSpecies) {
ret.push(...te.luckBoostedSpecies.filter(s => !ret.includes(s))); ret.add(s);
} }
}); }
return ret; }
return Array.from(ret);
} }
areFusionsBoosted(): boolean { areFusionsBoosted(): boolean {
@ -574,45 +568,50 @@ export class TimedEventManager {
*/ */
getFixedBattleEventRewards(wave: number): string[] { getFixedBattleEventRewards(wave: number): string[] {
const ret: string[] = []; const ret: string[] = [];
timedEvents for (const te of timedEvents) {
.filter(te => this.isActive(te) && te.classicWaveRewards != null) if (this.isActive(te) && te.classicWaveRewards != null) {
.map(te => { ret.push(...te.classicWaveRewards.filter(cwr => cwr.wave === wave).map(cwr => cwr.type));
ret.push(...te.classicWaveRewards!.filter(cwr => cwr.wave === wave).map(cwr => cwr.type)); }
}); }
return ret; return ret;
} }
// Gets the extra shiny chance for trainers due to event (odds/65536) /**
* Get the extra shiny chance for trainers due to event
*/
getClassicTrainerShinyChance(): number { getClassicTrainerShinyChance(): number {
let ret = 0; let ret = 0;
const tsEvents = timedEvents.filter(te => this.isActive(te) && te.trainerShinyChance != null); for (const te of timedEvents) {
tsEvents.map(t => (ret += t.trainerShinyChance!)); const shinyChance = te.trainerShinyChance;
if (shinyChance && this.isActive(te)) {
ret += shinyChance;
}
}
return ret; return ret;
} }
getEventBgmReplacement(bgm: string): string { getEventBgmReplacement(bgm: string): string {
let ret = bgm; let ret = bgm;
timedEvents.map(te => { for (const te of timedEvents) {
if (this.isActive(te) && te.music != null) { if (this.isActive(te) && te.music != null) {
te.music.map(mr => { for (const mr of te.music) {
if (mr[0] === bgm) { if (mr[0] === bgm) {
console.log(`it is ${te.name} so instead of ${mr[0]} we play ${mr[1]}`); console.log(`it is ${te.name} so instead of ${mr[0]} we play ${mr[1]}`);
ret = mr[1]; ret = mr[1];
} }
}); }
} }
}); }
return ret; return ret;
} }
/** /**
* Activates any challenges on {@linkcode globalScene.gameMode} for the currently active event * Activate any challenges on {@linkcode globalScene.gameMode} for the currently active event
*/ */
startEventChallenges(): void { startEventChallenges(): void {
const challenges = this.activeEvent()?.dailyRunChallenges; for (const eventChal of this.activeEvent()?.dailyRunChallenges ?? []) {
challenges?.forEach((eventChal: EventChallenge) => globalScene.gameMode.setChallengeValue(eventChal.challenge, eventChal.value);
globalScene.gameMode.setChallengeValue(eventChal.challenge, eventChal.value), }
);
} }
} }

View File

@ -240,8 +240,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
private passive: AbilityId; private passive: AbilityId;
private hasPassive: boolean; private hasPassive: boolean;
private hasAbilities: number[]; private hasAbilities: number[];
private biomes: BiomeTierTod[]; private biomes: readonly BiomeTierTod[];
private preBiomes: BiomeTierTod[]; private preBiomes: readonly BiomeTierTod[];
private baseStats: number[]; private baseStats: number[];
private baseTotal: number; private baseTotal: number;
private evolutions: SpeciesFormEvolution[]; private evolutions: SpeciesFormEvolution[];
@ -893,7 +893,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
} }
// Function to ensure that forms appear in the appropriate biome and tod // Function to ensure that forms appear in the appropriate biome and tod
sanitizeBiomes(biomes: BiomeTierTod[], speciesId: number): BiomeTierTod[] { sanitizeBiomes(biomes: readonly BiomeTierTod[], speciesId: number): readonly BiomeTierTod[] {
if (speciesId === SpeciesId.BURMY || speciesId === SpeciesId.WORMADAM) { if (speciesId === SpeciesId.BURMY || speciesId === SpeciesId.WORMADAM) {
return biomes.filter(b => { return biomes.filter(b => {
const formIndex = (() => { const formIndex = (() => {

View File

@ -16,22 +16,23 @@ interface hasPokemon {
* @returns The sorted array of {@linkcode Pokemon} * @returns The sorted array of {@linkcode Pokemon}
*/ */
export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[], shuffleFirst = true): T[] { export function sortInSpeedOrder<T extends Pokemon | hasPokemon>(pokemonList: T[], shuffleFirst = true): T[] {
pokemonList = shuffleFirst ? shufflePokemonList(pokemonList) : pokemonList; if (shuffleFirst) {
shufflePokemonList(pokemonList);
}
sortBySpeed(pokemonList); sortBySpeed(pokemonList);
return pokemonList; return pokemonList;
} }
/** /**
* Shuffle the list of pokemon *in place*
* @param pokemonList - The array of Pokemon or objects containing Pokemon * @param pokemonList - The array of Pokemon or objects containing Pokemon
* @returns The shuffled array * @returns The same array instance that was passed in, shuffled.
*/ */
function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] { function shufflePokemonList<T extends Pokemon | hasPokemon>(pokemonList: T[]): T[] {
// This is seeded with the current turn to prevent an inconsistency where it // This is seeded with the current turn to prevent an inconsistency where it
// was varying based on how long since you last reloaded // was varying based on how long since you last reloaded
globalScene.executeWithSeedOffset( globalScene.executeWithSeedOffset(
() => { () => randSeedShuffle(pokemonList),
pokemonList = randSeedShuffle(pokemonList);
},
globalScene.currentBattle.turn * 1000 + pokemonList.length, globalScene.currentBattle.turn * 1000 + pokemonList.length,
globalScene.waveSeed, globalScene.waveSeed,
); );