From 1ddcca7da57e5485217c123692bef72f0f1f8381 Mon Sep 17 00:00:00 2001 From: Wlowscha <54003515+Wlowscha@users.noreply.github.com> Date: Sat, 16 Aug 2025 19:14:40 +0200 Subject: [PATCH] Introduced `getSpeciesData` function --- src/data/challenge.ts | 61 ++++++++++++++++++++++++++++- src/enums/challenge-type.ts | 5 +++ src/system/game-data.ts | 8 ++-- src/ui/starter-select-ui-handler.ts | 42 ++++++++++++++------ src/utils/challenge-utils.ts | 18 ++++++++- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 89435149d2f..0b643d4291e 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -3,9 +3,11 @@ import { getRandomTrainerFunc } from "#app/battle"; import { defaultStarterSpecies } from "#app/constants"; import { speciesStarterCosts } from "#balance/starters"; import type { PokemonSpecies } from "#data/pokemon-species"; +import { AbilityAttr } from "#enums/ability-attr"; import { BattleType } from "#enums/battle-type"; import { Challenges } from "#enums/challenges"; import { TypeColor, TypeShadow } from "#enums/color"; +import { DexAttr } from "#enums/dex-attr"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { ModifierTier } from "#enums/modifier-tier"; import { MoveId } from "#enums/move-id"; @@ -19,8 +21,9 @@ import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { Trainer } from "#field/trainer"; import type { ModifierTypeOption } from "#modifiers/modifier-type"; import { PokemonMove } from "#moves/pokemon-move"; -import type { DexAttrProps, GameData } from "#system/game-data"; +import type { DexAttrProps, GameData, StarterDataEntry } from "#system/game-data"; import { RibbonData, type RibbonFlag } from "#system/ribbons/ribbon-data"; +import type { DexEntry } from "#types/dex-data"; import { type BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common"; import { deepCopy } from "#utils/data"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils"; @@ -237,6 +240,15 @@ export abstract class Challenge { return false; } + /** + * An apply function for STARTER_SELECT_MODIFY challenges. Derived classes should alter this. + * @param _pokemon {@link Pokemon} The starter pokemon to modify. + * @returns {@link boolean} Whether this function did anything. + */ + applyStarterSelectModify(_dexEntry: DexEntry, _starterDataEntry: StarterDataEntry): boolean { + return false; + } + /** * An apply function for STARTER_MODIFY challenges. Derived classes should alter this. * @param _pokemon {@link Pokemon} The starter pokemon to modify. @@ -797,6 +809,53 @@ export class FreshStartChallenge extends Challenge { return true; } + applyStarterSelectModify(dexEntry: DexEntry, starterDataEntry: StarterDataEntry): boolean { + // Remove all egg moves + starterDataEntry.eggMoves = 0; + console.log("I AM APPLYING, ", starterDataEntry.eggMoves); + + // Remove hidden and passive ability + const defaultAbilities = AbilityAttr.ABILITY_1 | AbilityAttr.ABILITY_2; + starterDataEntry.abilityAttr &= defaultAbilities; + starterDataEntry.passiveAttr = 0; + + // Remove cost reduction + starterDataEntry.valueReduction = 0; + + // Remove natures except for the default ones + const neutralNaturesAttr = + (1 << (Nature.HARDY + 1)) | + (1 << (Nature.DOCILE + 1)) | + (1 << (Nature.SERIOUS + 1)) | + (1 << (Nature.BASHFUL + 1)) | + (1 << (Nature.QUIRKY + 1)); + dexEntry.natureAttr &= neutralNaturesAttr; + + // Set all ivs to 15 + dexEntry.ivs = [15, 15, 15, 15, 15, 15]; + + // Removes shiny, variants, and any unlocked forms + const defaultDexEntry = DexAttr.NON_SHINY | DexAttr.MALE | DexAttr.FEMALE | DexAttr.DEFAULT_FORM; + dexEntry.caughtAttr &= defaultDexEntry; + + /** + let validMoves = pokemon.species + .getLevelMoves() + .filter(m => isBetween(m[0], 1, 5)) + .map(lm => lm[1]); + // Filter egg moves out of the moveset + pokemon.moveset = pokemon.moveset.filter(pm => validMoves.includes(pm.moveId)); + if (pokemon.moveset.length < 4) { + // If there's empty slots fill with remaining valid moves + const existingMoveIds = pokemon.moveset.map(pm => pm.moveId); + validMoves = validMoves.filter(m => !existingMoveIds.includes(m)); + pokemon.moveset = pokemon.moveset.concat(validMoves.map(m => new PokemonMove(m))).slice(0, 4); + } + pokemon.teraType = pokemon.species.type1; // Always primary tera type + */ + return true; + } + applyStarterModify(pokemon: Pokemon): boolean { pokemon.abilityIndex = pokemon.abilityIndex % 2; // Always base ability, if you set it to hidden it wraps to first ability pokemon.passive = false; // Passive isn't unlocked diff --git a/src/enums/challenge-type.ts b/src/enums/challenge-type.ts index 053bcf92011..f3a4b7c68f9 100644 --- a/src/enums/challenge-type.ts +++ b/src/enums/challenge-type.ts @@ -18,6 +18,11 @@ export enum ChallengeType { * @see {@link Challenge.applyStarterPointCost} */ STARTER_COST, + /** + * Challenges which modify the starter data in starter select + * @see {@link Challenge.applyStarterSelectModify} + */ + STARTER_SELECT_MODIFY, /** * Challenges which modify your starters in some way * @see {@link Challenge.applyStarterModify} diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 90cbf6e18cc..16f40b1b907 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -64,7 +64,7 @@ import { trainerConfigs } from "#trainers/trainer-config"; import type { DexData, DexEntry } from "#types/dex-data"; import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler"; import { applyChallenges } from "#utils/challenge-utils"; -import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common"; +import { executeIf, fixedInt, isLocal, isNullOrUndefined, NumberHolder, randInt, randSeedItem } from "#utils/common"; import { decrypt, encrypt } from "#utils/data"; import { getEnumKeys } from "#utils/enums"; import { getPokemonSpecies } from "#utils/pokemon-utils"; @@ -2103,8 +2103,10 @@ export class GameData { }; } - getStarterSpeciesDefaultAbilityIndex(species: PokemonSpecies): number { - const abilityAttr = this.starterData[species.speciesId].abilityAttr; + getStarterSpeciesDefaultAbilityIndex(species: PokemonSpecies, abilityAttr?: number): number { + if (isNullOrUndefined(abilityAttr)) { + abilityAttr = this.starterData[species.speciesId].abilityAttr; + } return abilityAttr & AbilityAttr.ABILITY_1 ? 0 : !species.ability2 || abilityAttr & AbilityAttr.ABILITY_2 ? 1 : 2; } diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 82467506720..9635804ba16 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -44,7 +44,7 @@ import { BattleSceneEventType } from "#events/battle-scene"; import type { Variant } from "#sprites/variant"; import { getVariantIcon, getVariantTint } from "#sprites/variant"; import { achvs } from "#system/achv"; -import type { DexAttrProps, StarterAttributes, StarterMoveset } from "#system/game-data"; +import type { DexAttrProps, StarterAttributes, StarterDataEntry, StarterMoveset } from "#system/game-data"; import { RibbonData } from "#system/ribbons/ribbon-data"; import { SettingKeyboard } from "#system/settings-keyboard"; import type { DexEntry } from "#types/dex-data"; @@ -1141,7 +1141,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.allSpecies.forEach((species, s) => { const icon = this.starterContainers[s].icon; - const dexEntry = globalScene.gameData.dexData[species.speciesId]; + const { dexEntry } = this.getSpeciesData(species.speciesId); // Initialize the StarterAttributes for this species this.starterPreferences[species.speciesId] = this.initStarterPrefs(species); @@ -1714,7 +1714,8 @@ export class StarterSelectUiHandler extends MessageUiHandler { globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), this.isPartyValid(), ); - const isCaught = globalScene.gameData.dexData[species.speciesId].caughtAttr; + const { dexEntry } = this.getSpeciesData(species.speciesId); + const isCaught = dexEntry.caughtAttr; return ( !isDupe && isValidForChallenge && currentPartyValue + starterCost <= this.getValueLimit() && isCaught ); @@ -2994,8 +2995,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { container.cost = globalScene.gameData.getSpeciesStarterValue(container.species.speciesId); // First, ensure you have the caught attributes for the species else default to bigint 0 - const caughtAttr = globalScene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0); - const starterData = globalScene.gameData.starterData[container.species.speciesId]; + const { dexEntry, starterDataEntry } = this.getSpeciesData(container.species.speciesId); + const caughtAttr = dexEntry?.caughtAttr || BigInt(0); + const starterData = starterDataEntry; const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId); // Gen filter @@ -3393,7 +3395,11 @@ export class StarterSelectUiHandler extends MessageUiHandler { } setSpecies(species: PokemonSpecies | null) { - this.speciesStarterDexEntry = species ? globalScene.gameData.dexData[species.speciesId] : null; + this.speciesStarterDexEntry = null; + if (species) { + const { dexEntry } = this.getSpeciesData(species.speciesId); + this.speciesStarterDexEntry = dexEntry; + } this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n; this.abilityCursor = species ? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.natureCursor = species ? globalScene.gameData.getSpeciesDefaultNature(species) : 0; @@ -3663,6 +3669,17 @@ export class StarterSelectUiHandler extends MessageUiHandler { } } + getSpeciesData(speciesId: SpeciesId): { dexEntry: DexEntry; starterDataEntry: StarterDataEntry } { + const dexEntry = globalScene.gameData.dexData[speciesId]; + const starterDataEntry = globalScene.gameData.starterData[speciesId]; + + const copiedDexEntry = { ...dexEntry }; + const copiedStarterDataEntry = { ...starterDataEntry }; + applyChallenges(ChallengeType.STARTER_SELECT_MODIFY, copiedDexEntry, copiedStarterDataEntry); + + return { dexEntry: { ...copiedDexEntry }, starterDataEntry: { ...copiedStarterDataEntry } }; + } + setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void { let { shiny, formIndex, female, variant, abilityIndex, natureIndex, teraType } = options; const forSeen: boolean = options.forSeen ?? false; @@ -3740,12 +3757,12 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.speciesStarterMoves = []; if (species) { - const dexEntry = globalScene.gameData.dexData[species.speciesId]; - const abilityAttr = globalScene.gameData.starterData[species.speciesId].abilityAttr; + const { dexEntry, starterDataEntry } = this.getSpeciesData(species.speciesId); + const caughtAttr = dexEntry.caughtAttr || BigInt(0); + const abilityAttr = starterDataEntry.abilityAttr; + console.log("I HAVE APPLIED, ", starterDataEntry.eggMoves); - const caughtAttr = globalScene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0); - - if (!dexEntry.caughtAttr) { + if (!caughtAttr) { const props = globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); const defaultAbilityIndex = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const defaultNature = globalScene.gameData.getSpeciesDefaultNature(species); @@ -4382,7 +4399,8 @@ export class StarterSelectUiHandler extends MessageUiHandler { */ getCurrentDexProps(speciesId: number): bigint { let props = 0n; - const caughtAttr = globalScene.gameData.dexData[speciesId].caughtAttr; + const { dexEntry } = this.getSpeciesData(speciesId); + const caughtAttr = dexEntry.caughtAttr; /* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props * It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props diff --git a/src/utils/challenge-utils.ts b/src/utils/challenge-utils.ts index c4fac3a0323..93768f673d9 100644 --- a/src/utils/challenge-utils.ts +++ b/src/utils/challenge-utils.ts @@ -10,7 +10,8 @@ import type { MoveSourceType } from "#enums/move-source-type"; import type { SpeciesId } from "#enums/species-id"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import type { ModifierTypeOption } from "#modifiers/modifier-type"; -import type { DexAttrProps } from "#system/game-data"; +import type { DexAttrProps, StarterDataEntry } from "#system/game-data"; +import type { DexEntry } from "#types/dex-data"; import { BooleanHolder, type NumberHolder } from "./common"; import { getPokemonSpecies } from "./pokemon-utils"; @@ -47,6 +48,18 @@ export function applyChallenges( species: SpeciesId, cost: NumberHolder, ): boolean; +/** + * Apply all challenges that modify selectable starter data. + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_SELECT_MODIFY + * @param dexEntry {@link DexEntry} The pokedex data associated to the pokemon. + * @param starterDataEntry {@link StarterDataEntry} The starter data associated to the pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.STARTER_SELECT_MODIFY, + dexEntry: DexEntry, + starterDataEntry: StarterDataEntry, +): boolean; /** * Apply all challenges that modify a starter after selection. * @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY @@ -269,6 +282,9 @@ export function applyChallenges(challengeType: ChallengeType, ...args: any[]): b case ChallengeType.STARTER_COST: ret ||= c.applyStarterCost(args[0], args[1]); break; + case ChallengeType.STARTER_SELECT_MODIFY: + ret ||= c.applyStarterSelectModify(args[0], args[1]); + break; case ChallengeType.STARTER_MODIFY: ret ||= c.applyStarterModify(args[0]); break;