This commit is contained in:
Wlowscha 2025-08-18 18:38:16 -07:00 committed by GitHub
commit 81abc30fda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 245 additions and 86 deletions

View File

@ -3,9 +3,11 @@ import { getRandomTrainerFunc } from "#app/battle";
import { defaultStarterSpecies } from "#app/constants"; import { defaultStarterSpecies } from "#app/constants";
import { speciesStarterCosts } from "#balance/starters"; import { speciesStarterCosts } from "#balance/starters";
import type { PokemonSpecies } from "#data/pokemon-species"; import type { PokemonSpecies } from "#data/pokemon-species";
import { AbilityAttr } from "#enums/ability-attr";
import { BattleType } from "#enums/battle-type"; import { BattleType } from "#enums/battle-type";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { TypeColor, TypeShadow } from "#enums/color"; import { TypeColor, TypeShadow } from "#enums/color";
import { DexAttr } from "#enums/dex-attr";
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
import { ModifierTier } from "#enums/modifier-tier"; import { ModifierTier } from "#enums/modifier-tier";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
@ -19,8 +21,9 @@ import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer"; import { Trainer } from "#field/trainer";
import type { ModifierTypeOption } from "#modifiers/modifier-type"; import type { ModifierTypeOption } from "#modifiers/modifier-type";
import { PokemonMove } from "#moves/pokemon-move"; 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 { 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 { type BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common";
import { deepCopy } from "#utils/data"; import { deepCopy } from "#utils/data";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
@ -237,6 +240,15 @@ export abstract class Challenge {
return false; 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(_speciesId: SpeciesId, _dexEntry: DexEntry, _starterDataEntry: StarterDataEntry): boolean {
return false;
}
/** /**
* An apply function for STARTER_MODIFY challenges. Derived classes should alter this. * An apply function for STARTER_MODIFY challenges. Derived classes should alter this.
* @param _pokemon {@link Pokemon} The starter pokemon to modify. * @param _pokemon {@link Pokemon} The starter pokemon to modify.
@ -797,10 +809,60 @@ export class FreshStartChallenge extends Challenge {
return true; return true;
} }
applyStarterSelectModify(speciesId: SpeciesId, 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 and variants
dexEntry.caughtAttr &= ~DexAttr.SHINY;
dexEntry.caughtAttr &= ~(DexAttr.VARIANT_2 | DexAttr.VARIANT_3);
// Remove unlocked forms for specific species
if (speciesId === SpeciesId.ZYGARDE) {
const formMask = (DexAttr.DEFAULT_FORM << 2n) - 1n; // Sets 10%-PC to 10%-AB and 50%-PC to 50%-AB
dexEntry.caughtAttr &= formMask;
}
if (
[
SpeciesId.PIKACHU,
SpeciesId.EEVEE,
SpeciesId.PICHU,
SpeciesId.ROTOM,
SpeciesId.MELOETTA,
SpeciesId.FROAKIE,
].includes(speciesId)
) {
const formMask = (DexAttr.DEFAULT_FORM << 1n) - 1n; // These mons are set to form 0 because they're meant to be unlocks or mid-run form changes
dexEntry.caughtAttr &= formMask;
}
return true;
}
applyStarterModify(pokemon: Pokemon): boolean { applyStarterModify(pokemon: Pokemon): boolean {
pokemon.abilityIndex = pokemon.abilityIndex % 2; // Always base ability, if you set it to hidden it wraps to first ability 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 pokemon.passive = false; // Passive isn't unlocked
pokemon.nature = Nature.HARDY; // Neutral nature
let validMoves = pokemon.species let validMoves = pokemon.species
.getLevelMoves() .getLevelMoves()
.filter(m => isBetween(m[0], 1, 5)) .filter(m => isBetween(m[0], 1, 5))
@ -827,7 +889,6 @@ export class FreshStartChallenge extends Challenge {
SpeciesId.ROTOM, SpeciesId.ROTOM,
SpeciesId.MELOETTA, SpeciesId.MELOETTA,
SpeciesId.FROAKIE, SpeciesId.FROAKIE,
SpeciesId.ROCKRUFF,
].includes(pokemon.species.speciesId) ].includes(pokemon.species.speciesId)
) { ) {
pokemon.formIndex = 0; // These mons are set to form 0 because they're meant to be unlocks or mid-run form changes pokemon.formIndex = 0; // These mons are set to form 0 because they're meant to be unlocks or mid-run form changes

View File

@ -18,6 +18,11 @@ export enum ChallengeType {
* @see {@link Challenge.applyStarterPointCost} * @see {@link Challenge.applyStarterPointCost}
*/ */
STARTER_COST, 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 * Challenges which modify your starters in some way
* @see {@link Challenge.applyStarterModify} * @see {@link Challenge.applyStarterModify}

View File

@ -64,7 +64,7 @@ import { trainerConfigs } from "#trainers/trainer-config";
import type { DexData, DexEntry } from "#types/dex-data"; import type { DexData, DexEntry } from "#types/dex-data";
import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler"; import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler";
import { applyChallenges } from "#utils/challenge-utils"; 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 { decrypt, encrypt } from "#utils/data";
import { getEnumKeys } from "#utils/enums"; import { getEnumKeys } from "#utils/enums";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";
@ -2103,13 +2103,17 @@ export class GameData {
}; };
} }
getStarterSpeciesDefaultAbilityIndex(species: PokemonSpecies): number { getStarterSpeciesDefaultAbilityIndex(species: PokemonSpecies, abilityAttr?: number): number {
const abilityAttr = this.starterData[species.speciesId].abilityAttr; if (isNullOrUndefined(abilityAttr)) {
abilityAttr = this.starterData[species.speciesId].abilityAttr;
}
return abilityAttr & AbilityAttr.ABILITY_1 ? 0 : !species.ability2 || abilityAttr & AbilityAttr.ABILITY_2 ? 1 : 2; return abilityAttr & AbilityAttr.ABILITY_1 ? 0 : !species.ability2 || abilityAttr & AbilityAttr.ABILITY_2 ? 1 : 2;
} }
getSpeciesDefaultNature(species: PokemonSpecies): Nature { getSpeciesDefaultNature(species: PokemonSpecies, dexEntry?: DexEntry): Nature {
const dexEntry = this.dexData[species.speciesId]; if (isNullOrUndefined(dexEntry)) {
dexEntry = this.dexData[species.speciesId];
}
for (let n = 0; n < 25; n++) { for (let n = 0; n < 25; n++) {
if (dexEntry.natureAttr & (1 << (n + 1))) { if (dexEntry.natureAttr & (1 << (n + 1))) {
return n as Nature; return n as Nature;

View File

@ -27,6 +27,7 @@ import { AbilityAttr } from "#enums/ability-attr";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { ChallengeType } from "#enums/challenge-type"; import { ChallengeType } from "#enums/challenge-type";
import { Challenges } from "#enums/challenges";
import { Device } from "#enums/devices"; import { Device } from "#enums/devices";
import { DexAttr } from "#enums/dex-attr"; import { DexAttr } from "#enums/dex-attr";
import { DropDownColumn } from "#enums/drop-down-column"; import { DropDownColumn } from "#enums/drop-down-column";
@ -44,7 +45,7 @@ import { BattleSceneEventType } from "#events/battle-scene";
import type { Variant } from "#sprites/variant"; import type { Variant } from "#sprites/variant";
import { getVariantIcon, getVariantTint } from "#sprites/variant"; import { getVariantIcon, getVariantTint } from "#sprites/variant";
import { achvs } from "#system/achv"; 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 { RibbonData } from "#system/ribbons/ribbon-data";
import { SettingKeyboard } from "#system/settings-keyboard"; import { SettingKeyboard } from "#system/settings-keyboard";
import type { DexEntry } from "#types/dex-data"; import type { DexEntry } from "#types/dex-data";
@ -400,8 +401,10 @@ export class StarterSelectUiHandler extends MessageUiHandler {
private starterSelectCallback: StarterSelectCallback | null; private starterSelectCallback: StarterSelectCallback | null;
private starterPreferences: StarterPreferences; private starterPreferences: StarterPreferences;
private originalStarterPreferences: StarterPreferences;
protected blockInput = false; protected blockInput = false;
private allowTera: boolean;
constructor() { constructor() {
super(UiMode.STARTER_SELECT); super(UiMode.STARTER_SELECT);
@ -1126,25 +1129,32 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
show(args: any[]): boolean { show(args: any[]): boolean {
if (!this.starterPreferences) {
// starterPreferences haven't been loaded yet
this.starterPreferences = loadStarterPreferences();
}
this.moveInfoOverlay.clear(); // clear this when removing a menu; the cancel button doesn't seem to trigger this automatically on controllers this.moveInfoOverlay.clear(); // clear this when removing a menu; the cancel button doesn't seem to trigger this automatically on controllers
this.pokerusSpecies = getPokerusStarters(); this.pokerusSpecies = getPokerusStarters();
this.allowTera = globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id);
if (args.length >= 1 && args[0] instanceof Function) { if (args.length >= 1 && args[0] instanceof Function) {
super.show(args); super.show(args);
this.starterSelectCallback = args[0] as StarterSelectCallback; this.starterSelectCallback = args[0] as StarterSelectCallback;
this.starterSelectContainer.setVisible(true); this.starterSelectContainer.setVisible(true);
this.starterPreferences = loadStarterPreferences();
this.originalStarterPreferences = loadStarterPreferences();
console.log("Loaded", this.originalStarterPreferences[1]);
this.allSpecies.forEach((species, s) => { this.allSpecies.forEach((species, s) => {
const icon = this.starterContainers[s].icon; 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 // Initialize the StarterAttributes for this species
this.starterPreferences[species.speciesId] = this.initStarterPrefs(species); this.starterPreferences[species.speciesId] = this.initStarterPrefs(species, this.starterPreferences);
this.originalStarterPreferences[species.speciesId] = this.initStarterPrefs(
species,
this.originalStarterPreferences,
true,
);
if (dexEntry.caughtAttr) { if (dexEntry.caughtAttr) {
icon.clearTint(); icon.clearTint();
@ -1154,6 +1164,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.setUpgradeAnimation(icon, species); this.setUpgradeAnimation(icon, species);
}); });
console.log("Initiated", this.originalStarterPreferences[1]);
this.resetFilters(); this.resetFilters();
this.updateStarters(); this.updateStarters();
@ -1179,10 +1190,14 @@ export class StarterSelectUiHandler extends MessageUiHandler {
* @param species The species to get Starter Preferences for * @param species The species to get Starter Preferences for
* @returns StarterAttributes for the species * @returns StarterAttributes for the species
*/ */
initStarterPrefs(species: PokemonSpecies): StarterAttributes { initStarterPrefs(
const starterAttributes = this.starterPreferences[species.speciesId]; species: PokemonSpecies,
const dexEntry = globalScene.gameData.dexData[species.speciesId]; preferences: StarterPreferences,
const starterData = globalScene.gameData.starterData[species.speciesId]; ignoreChallenge = false,
): StarterAttributes {
const starterAttributes = preferences[species.speciesId];
const { dexEntry, starterDataEntry } = this.getSpeciesData(species.speciesId, !ignoreChallenge);
const starterData = starterDataEntry;
// no preferences or Pokemon wasn't caught, return empty attribute // no preferences or Pokemon wasn't caught, return empty attribute
if (!starterAttributes || !dexEntry.caughtAttr) { if (!starterAttributes || !dexEntry.caughtAttr) {
@ -1262,6 +1277,17 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} }
if (starterAttributes.tera !== undefined) {
// If somehow we have an illegal tera type, it is reset here
if (!(starterAttributes.tera === species.type1 || starterAttributes.tera === species?.type2)) {
starterAttributes.tera = species.type1;
}
// In fresh start challenge, the tera type is always reset to the first one
if (globalScene.gameMode.hasChallenge(Challenges.FRESH_START) && !ignoreChallenge) {
starterAttributes.tera = species.type1;
}
}
return starterAttributes; return starterAttributes;
} }
@ -1714,7 +1740,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
this.isPartyValid(), this.isPartyValid(),
); );
const isCaught = globalScene.gameData.dexData[species.speciesId].caughtAttr; const { dexEntry } = this.getSpeciesData(species.speciesId);
const isCaught = dexEntry.caughtAttr;
return ( return (
!isDupe && isValidForChallenge && currentPartyValue + starterCost <= this.getValueLimit() && isCaught !isDupe && isValidForChallenge && currentPartyValue + starterCost <= this.getValueLimit() && isCaught
); );
@ -1781,9 +1808,11 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} else { } else {
let starterContainer: StarterContainer; let starterContainer: StarterContainer;
const starterData = globalScene.gameData.starterData[this.lastSpecies.speciesId]; const { starterDataEntry } = this.getSpeciesData(this.lastSpecies.speciesId);
const starterData = starterDataEntry;
// prepare persistent starter data to store changes // prepare persistent starter data to store changes
let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId]; let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId];
const originalStarterAttributes = this.originalStarterPreferences[this.lastSpecies.speciesId];
// this gets the correct pokemon cursor depending on whether you're in the starter screen or the party icons // this gets the correct pokemon cursor depending on whether you're in the starter screen or the party icons
if (!this.starterIconsCursorObj.visible) { if (!this.starterIconsCursorObj.visible) {
@ -1999,6 +2028,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
starterAttributes = this.starterPreferences[this.lastSpecies.speciesId] = {}; starterAttributes = this.starterPreferences[this.lastSpecies.speciesId] = {};
} }
starterAttributes.nature = n; starterAttributes.nature = n;
originalStarterAttributes.nature = starterAttributes.nature;
this.clearText(); this.clearText();
ui.setMode(UiMode.STARTER_SELECT); ui.setMode(UiMode.STARTER_SELECT);
// set nature for starter // set nature for starter
@ -2067,6 +2097,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
label: i18next.t("starterSelectUiHandler:addToFavorites"), label: i18next.t("starterSelectUiHandler:addToFavorites"),
handler: () => { handler: () => {
starterAttributes.favorite = true; starterAttributes.favorite = true;
originalStarterAttributes.favorite = true;
// if the starter container not exists, it means the species is not in the filtered starters // if the starter container not exists, it means the species is not in the filtered starters
if (starterContainer) { if (starterContainer) {
starterContainer.favoriteIcon.setVisible(starterAttributes.favorite); starterContainer.favoriteIcon.setVisible(starterAttributes.favorite);
@ -2080,6 +2111,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
label: i18next.t("starterSelectUiHandler:removeFromFavorites"), label: i18next.t("starterSelectUiHandler:removeFromFavorites"),
handler: () => { handler: () => {
starterAttributes.favorite = false; starterAttributes.favorite = false;
originalStarterAttributes.favorite = false;
// if the starter container not exists, it means the species is not in the filtered starters // if the starter container not exists, it means the species is not in the filtered starters
if (starterContainer) { if (starterContainer) {
starterContainer.favoriteIcon.setVisible(starterAttributes.favorite); starterContainer.favoriteIcon.setVisible(starterAttributes.favorite);
@ -2102,6 +2134,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
(sanitizedName: string) => { (sanitizedName: string) => {
ui.playSelect(); ui.playSelect();
starterAttributes.nickname = sanitizedName; starterAttributes.nickname = sanitizedName;
originalStarterAttributes.nickname = sanitizedName;
const name = decodeURIComponent(escape(atob(starterAttributes.nickname))); const name = decodeURIComponent(escape(atob(starterAttributes.nickname)));
if (name.length > 0) { if (name.length > 0) {
this.pokemonNameText.setText(name); this.pokemonNameText.setText(name);
@ -2328,6 +2361,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)).setTint(tint).setVisible(true); this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)).setTint(tint).setVisible(true);
starterAttributes.shiny = true; starterAttributes.shiny = true;
originalStarterAttributes.shiny = true;
} else { } else {
// If shiny, we update the variant // If shiny, we update the variant
let newVariant = props.variant; let newVariant = props.variant;
@ -2351,6 +2385,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} while (newVariant !== props.variant); } while (newVariant !== props.variant);
starterAttributes.variant = newVariant; // store the selected variant starterAttributes.variant = newVariant; // store the selected variant
originalStarterAttributes.variant = newVariant;
if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY && newVariant <= props.variant) { if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY && newVariant <= props.variant) {
// If we have run out of variants, go back to non shiny // If we have run out of variants, go back to non shiny
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
@ -2360,6 +2395,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonShinyIcon.setVisible(false); this.pokemonShinyIcon.setVisible(false);
success = true; success = true;
starterAttributes.shiny = false; starterAttributes.shiny = false;
originalStarterAttributes.shiny = false;
} else { } else {
// If going to a higher variant, or only shiny forms are caught, go to next variant // If going to a higher variant, or only shiny forms are caught, go to next variant
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
@ -2388,7 +2424,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} while (newFormIndex !== props.formIndex); } while (newFormIndex !== props.formIndex);
starterAttributes.form = newFormIndex; // store the selected form starterAttributes.form = newFormIndex; // store the selected form
originalStarterAttributes.form = newFormIndex;
starterAttributes.tera = this.lastSpecies.forms[newFormIndex].type1; starterAttributes.tera = this.lastSpecies.forms[newFormIndex].type1;
originalStarterAttributes.tera = starterAttributes.tera;
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
formIndex: newFormIndex, formIndex: newFormIndex,
teraType: starterAttributes.tera, teraType: starterAttributes.tera,
@ -2399,6 +2437,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
case Button.CYCLE_GENDER: case Button.CYCLE_GENDER:
if (this.canCycleGender) { if (this.canCycleGender) {
starterAttributes.female = !props.female; starterAttributes.female = !props.female;
originalStarterAttributes.female = starterAttributes.female;
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
female: !props.female, female: !props.female,
}); });
@ -2408,7 +2447,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
case Button.CYCLE_ABILITY: case Button.CYCLE_ABILITY:
if (this.canCycleAbility) { if (this.canCycleAbility) {
const abilityCount = this.lastSpecies.getAbilityCount(); const abilityCount = this.lastSpecies.getAbilityCount();
const abilityAttr = globalScene.gameData.starterData[this.lastSpecies.speciesId].abilityAttr; const abilityAttr = starterDataEntry.abilityAttr;
const hasAbility1 = abilityAttr & AbilityAttr.ABILITY_1; const hasAbility1 = abilityAttr & AbilityAttr.ABILITY_1;
let newAbilityIndex = this.abilityCursor; let newAbilityIndex = this.abilityCursor;
do { do {
@ -2430,6 +2469,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} while (newAbilityIndex !== this.abilityCursor); } while (newAbilityIndex !== this.abilityCursor);
starterAttributes.ability = newAbilityIndex; // store the selected ability starterAttributes.ability = newAbilityIndex; // store the selected ability
originalStarterAttributes.ability = newAbilityIndex;
const { visible: tooltipVisible } = globalScene.ui.getTooltip(); const { visible: tooltipVisible } = globalScene.ui.getTooltip();
@ -2451,6 +2491,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const newNature = natures[natureIndex < natures.length - 1 ? natureIndex + 1 : 0]; const newNature = natures[natureIndex < natures.length - 1 ? natureIndex + 1 : 0];
// store cycled nature as default // store cycled nature as default
starterAttributes.nature = newNature as unknown as number; starterAttributes.nature = newNature as unknown as number;
originalStarterAttributes.nature = starterAttributes.nature;
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
natureIndex: newNature, natureIndex: newNature,
}); });
@ -2461,12 +2502,14 @@ export class StarterSelectUiHandler extends MessageUiHandler {
if (this.canCycleTera) { if (this.canCycleTera) {
const speciesForm = getPokemonSpeciesForm(this.lastSpecies.speciesId, starterAttributes.form ?? 0); const speciesForm = getPokemonSpeciesForm(this.lastSpecies.speciesId, starterAttributes.form ?? 0);
if (speciesForm.type1 === this.teraCursor && !isNullOrUndefined(speciesForm.type2)) { if (speciesForm.type1 === this.teraCursor && !isNullOrUndefined(speciesForm.type2)) {
starterAttributes.tera = speciesForm.type2!; starterAttributes.tera = speciesForm.type2;
originalStarterAttributes.tera = starterAttributes.tera;
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
teraType: speciesForm.type2!, teraType: speciesForm.type2,
}); });
} else { } else {
starterAttributes.tera = speciesForm.type1; starterAttributes.tera = speciesForm.type1;
originalStarterAttributes.tera = starterAttributes.tera;
this.setSpeciesDetails(this.lastSpecies, { this.setSpeciesDetails(this.lastSpecies, {
teraType: speciesForm.type1, teraType: speciesForm.type1,
}); });
@ -2713,16 +2756,16 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
const updatedMoveset = starterMoveset.slice() as StarterMoveset; const updatedMoveset = starterMoveset.slice() as StarterMoveset;
const formIndex = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor).formIndex; const formIndex = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor).formIndex;
const starterData = globalScene.gameData.starterData[speciesId]; const starterDataEntry = globalScene.gameData.starterData[speciesId];
// species has different forms // species has different forms
if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) { if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) {
// Species has forms with different movesets // Species has forms with different movesets
if (!starterData.moveset || Array.isArray(starterData.moveset)) { if (!starterDataEntry.moveset || Array.isArray(starterDataEntry.moveset)) {
starterData.moveset = {}; starterDataEntry.moveset = {};
} }
starterData.moveset[formIndex] = updatedMoveset; starterDataEntry.moveset[formIndex] = updatedMoveset;
} else { } else {
starterData.moveset = updatedMoveset; starterDataEntry.moveset = updatedMoveset;
} }
this.setSpeciesDetails(this.lastSpecies, { forSeen: false }); this.setSpeciesDetails(this.lastSpecies, { forSeen: false });
@ -2994,8 +3037,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
container.cost = globalScene.gameData.getSpeciesStarterValue(container.species.speciesId); container.cost = globalScene.gameData.getSpeciesStarterValue(container.species.speciesId);
// First, ensure you have the caught attributes for the species else default to bigint 0 // 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 { dexEntry, starterDataEntry } = this.getSpeciesData(container.species.speciesId);
const starterData = globalScene.gameData.starterData[container.species.speciesId]; const caughtAttr = dexEntry?.caughtAttr || BigInt(0);
const starterData = starterDataEntry;
const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId); const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId);
// Gen filter // Gen filter
@ -3227,12 +3271,12 @@ export class StarterSelectUiHandler extends MessageUiHandler {
onScreenFirstIndex + maxRows * maxColumns - 1, onScreenFirstIndex + maxRows * maxColumns - 1,
); );
const gameData = globalScene.gameData;
this.starterSelectScrollBar.setScrollCursor(this.scrollCursor); this.starterSelectScrollBar.setScrollCursor(this.scrollCursor);
let pokerusCursorIndex = 0; let pokerusCursorIndex = 0;
this.filteredStarterContainers.forEach((container, i) => { this.filteredStarterContainers.forEach((container, i) => {
const { dexEntry, starterDataEntry } = this.getSpeciesData(container.species.speciesId);
const pos = calcStarterPosition(i, this.scrollCursor); const pos = calcStarterPosition(i, this.scrollCursor);
container.setPosition(pos.x, pos.y); container.setPosition(pos.x, pos.y);
if (i < onScreenFirstIndex || i > onScreenLastIndex) { if (i < onScreenFirstIndex || i > onScreenLastIndex) {
@ -3268,10 +3312,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
container.label.setVisible(true); container.label.setVisible(true);
const speciesVariants = const speciesVariants =
speciesId && gameData.dexData[speciesId].caughtAttr & DexAttr.SHINY speciesId && dexEntry.caughtAttr & DexAttr.SHINY
? [DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3].filter( ? [DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3].filter(v => !!(dexEntry.caughtAttr & v))
v => !!(gameData.dexData[speciesId].caughtAttr & v),
)
: []; : [];
for (let v = 0; v < 3; v++) { for (let v = 0; v < 3; v++) {
const hasVariant = speciesVariants.length > v; const hasVariant = speciesVariants.length > v;
@ -3285,15 +3327,11 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} }
container.starterPassiveBgs.setVisible(!!gameData.starterData[speciesId].passiveAttr); container.starterPassiveBgs.setVisible(!!starterDataEntry.passiveAttr);
container.hiddenAbilityIcon.setVisible( container.hiddenAbilityIcon.setVisible(!!dexEntry.caughtAttr && !!(starterDataEntry.abilityAttr & 4));
!!gameData.dexData[speciesId].caughtAttr && !!(gameData.starterData[speciesId].abilityAttr & 4),
);
container.classicWinIcon container.classicWinIcon
.setVisible(gameData.starterData[speciesId].classicWinCount > 0) .setVisible(starterDataEntry.classicWinCount > 0)
.setTexture( .setTexture(dexEntry.ribbons.has(RibbonData.NUZLOCKE) ? "champion_ribbon_emerald" : "champion_ribbon");
gameData.dexData[speciesId].ribbons.has(RibbonData.NUZLOCKE) ? "champion_ribbon_emerald" : "champion_ribbon",
);
container.favoriteIcon.setVisible(this.starterPreferences[speciesId]?.favorite ?? false); container.favoriteIcon.setVisible(this.starterPreferences[speciesId]?.favorite ?? false);
// 'Candy Icon' mode // 'Candy Icon' mode
@ -3393,11 +3431,20 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
setSpecies(species: PokemonSpecies | null) { setSpecies(species: PokemonSpecies | null) {
this.speciesStarterDexEntry = species ? globalScene.gameData.dexData[species.speciesId] : null; this.speciesStarterDexEntry = null;
this.dexAttrCursor = species ? this.getCurrentDexProps(species.speciesId) : 0n; this.dexAttrCursor = 0n;
this.abilityCursor = species ? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.abilityCursor = 0;
this.natureCursor = species ? globalScene.gameData.getSpeciesDefaultNature(species) : 0; this.natureCursor = 0;
this.teraCursor = species ? species.type1 : PokemonType.UNKNOWN; this.teraCursor = PokemonType.UNKNOWN;
if (species) {
const { dexEntry } = this.getSpeciesData(species.speciesId);
this.speciesStarterDexEntry = dexEntry;
this.dexAttrCursor = this.getCurrentDexProps(species.speciesId);
this.abilityCursor = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
this.natureCursor = globalScene.gameData.getSpeciesDefaultNature(species, dexEntry);
this.teraCursor = species.type1;
}
if (!species && globalScene.ui.getTooltip().visible) { if (!species && globalScene.ui.getTooltip().visible) {
globalScene.ui.hideTooltip(); globalScene.ui.hideTooltip();
@ -3562,11 +3609,12 @@ export class StarterSelectUiHandler extends MessageUiHandler {
teraType: this.starterTeras[starterIndex], teraType: this.starterTeras[starterIndex],
}); });
} else { } else {
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
const defaultAbilityIndex = const defaultAbilityIndex =
starterAttributes?.ability ?? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species); starterAttributes?.ability ?? globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
// load default nature from stater save data, if set // load default nature from stater save data, if set
const defaultNature = starterAttributes?.nature || globalScene.gameData.getSpeciesDefaultNature(species); const { dexEntry } = this.getSpeciesData(species.speciesId);
const defaultNature =
starterAttributes?.nature || globalScene.gameData.getSpeciesDefaultNature(species, dexEntry);
props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); props = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) { if (starterAttributes?.variant && !Number.isNaN(starterAttributes.variant)) {
if (props.shiny) { if (props.shiny) {
@ -3583,6 +3631,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
variant: props.variant, variant: props.variant,
abilityIndex: defaultAbilityIndex, abilityIndex: defaultAbilityIndex,
natureIndex: defaultNature, natureIndex: defaultNature,
teraType: starterAttributes?.tera,
}); });
} }
@ -3663,14 +3712,34 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
} }
getSpeciesData(
speciesId: SpeciesId,
applyChallenge = true,
): { dexEntry: DexEntry; starterDataEntry: StarterDataEntry } {
const dexEntry = globalScene.gameData.dexData[speciesId];
const starterDataEntry = globalScene.gameData.starterData[speciesId];
const copiedDexEntry = { ...dexEntry };
const copiedStarterDataEntry = { ...starterDataEntry };
if (applyChallenge) {
applyChallenges(ChallengeType.STARTER_SELECT_MODIFY, speciesId, copiedDexEntry, copiedStarterDataEntry);
}
return { dexEntry: { ...copiedDexEntry }, starterDataEntry: { ...copiedStarterDataEntry } };
}
setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void { setSpeciesDetails(species: PokemonSpecies, options: SpeciesDetails = {}): void {
let { shiny, formIndex, female, variant, abilityIndex, natureIndex, teraType } = options; let { shiny, formIndex, female, variant, abilityIndex, natureIndex, teraType } = options;
const forSeen: boolean = options.forSeen ?? false; const forSeen: boolean = options.forSeen ?? false;
const oldProps = species ? globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null; const oldProps = species ? globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
const oldAbilityIndex = const oldAbilityIndex =
this.abilityCursor > -1 ? this.abilityCursor : globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species); this.abilityCursor > -1 ? this.abilityCursor : globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
const oldNatureIndex = let oldNatureIndex = -1;
this.natureCursor > -1 ? this.natureCursor : globalScene.gameData.getSpeciesDefaultNature(species); if (species) {
const { dexEntry } = this.getSpeciesData(species.speciesId);
oldNatureIndex =
this.natureCursor > -1 ? this.natureCursor : globalScene.gameData.getSpeciesDefaultNature(species, dexEntry);
}
const oldTeraType = this.teraCursor > -1 ? this.teraCursor : species ? species.type1 : PokemonType.UNKNOWN;
this.dexAttrCursor = 0n; this.dexAttrCursor = 0n;
this.abilityCursor = -1; this.abilityCursor = -1;
this.natureCursor = -1; this.natureCursor = -1;
@ -3717,7 +3786,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
); // TODO: is this bang correct? ); // TODO: is this bang correct?
this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex); this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex);
this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex);
this.teraCursor = !isNullOrUndefined(teraType) ? teraType : (teraType = species.type1); this.teraCursor = !isNullOrUndefined(teraType) ? teraType : (teraType = oldTeraType);
const [isInParty, partyIndex]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image const [isInParty, partyIndex]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image
if (isInParty) { if (isInParty) {
this.updatePartyIcon(species, partyIndex); this.updatePartyIcon(species, partyIndex);
@ -3740,15 +3809,14 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.speciesStarterMoves = []; this.speciesStarterMoves = [];
if (species) { if (species) {
const dexEntry = globalScene.gameData.dexData[species.speciesId]; const { dexEntry, starterDataEntry } = this.getSpeciesData(species.speciesId);
const abilityAttr = globalScene.gameData.starterData[species.speciesId].abilityAttr; const caughtAttr = dexEntry.caughtAttr || BigInt(0);
const abilityAttr = starterDataEntry.abilityAttr;
const caughtAttr = globalScene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0); if (!caughtAttr) {
if (!dexEntry.caughtAttr) {
const props = globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); const props = globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
const defaultAbilityIndex = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const defaultAbilityIndex = globalScene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
const defaultNature = globalScene.gameData.getSpeciesDefaultNature(species); const defaultNature = globalScene.gameData.getSpeciesDefaultNature(species, dexEntry);
if (shiny === undefined || shiny !== props.shiny) { if (shiny === undefined || shiny !== props.shiny) {
shiny = props.shiny; shiny = props.shiny;
@ -3858,8 +3926,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1; this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
this.canCycleTera = this.canCycleTera =
!this.statsMode && !this.statsMode &&
globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && this.allowTera &&
!isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2); !isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2) &&
!globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
} }
if (dexEntry.caughtAttr && species.malePercent !== null) { if (dexEntry.caughtAttr && species.malePercent !== null) {
@ -3886,7 +3955,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD)) .setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD))
.setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true)); .setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true));
const passiveAttr = globalScene.gameData.starterData[species.speciesId].passiveAttr; const passiveAttr = starterDataEntry.passiveAttr;
const passiveAbility = allAbilities[this.lastSpecies.getPassiveAbility(formIndex)]; const passiveAbility = allAbilities[this.lastSpecies.getPassiveAbility(formIndex)];
if (this.pokemonAbilityText.visible) { if (this.pokemonAbilityText.visible) {
@ -3967,13 +4036,13 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] > 0 && lm[0] <= 5).map(lm => lm[1])); this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] > 0 && lm[0] <= 5).map(lm => lm[1]));
if (speciesEggMoves.hasOwnProperty(species.speciesId)) { if (speciesEggMoves.hasOwnProperty(species.speciesId)) {
for (let em = 0; em < 4; em++) { for (let em = 0; em < 4; em++) {
if (globalScene.gameData.starterData[species.speciesId].eggMoves & (1 << em)) { if (starterDataEntry.eggMoves & (1 << em)) {
this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]); this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]);
} }
} }
} }
const speciesMoveData = globalScene.gameData.starterData[species.speciesId].moveset; const speciesMoveData = starterDataEntry.moveset;
const moveData: StarterMoveset | null = speciesMoveData const moveData: StarterMoveset | null = speciesMoveData
? Array.isArray(speciesMoveData) ? Array.isArray(speciesMoveData)
? speciesMoveData ? speciesMoveData
@ -3981,9 +4050,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
: null; : null;
const availableStarterMoves = this.speciesStarterMoves.concat( const availableStarterMoves = this.speciesStarterMoves.concat(
speciesEggMoves.hasOwnProperty(species.speciesId) speciesEggMoves.hasOwnProperty(species.speciesId)
? speciesEggMoves[species.speciesId].filter( ? speciesEggMoves[species.speciesId].filter((_: any, em: number) => starterDataEntry.eggMoves & (1 << em))
(_: any, em: number) => globalScene.gameData.starterData[species.speciesId].eggMoves & (1 << em),
)
: [], : [],
); );
this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m =>
@ -4010,9 +4077,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.setTypeIcons(speciesForm.type1, speciesForm.type2); this.setTypeIcons(speciesForm.type1, speciesForm.type2);
this.teraIcon.setFrame(PokemonType[this.teraCursor].toLowerCase()); this.teraIcon.setFrame(PokemonType[this.teraCursor].toLowerCase());
this.teraIcon.setVisible( this.teraIcon.setVisible(!this.statsMode && this.allowTera);
!this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id),
);
} else { } else {
this.pokemonAbilityText.setText(""); this.pokemonAbilityText.setText("");
this.pokemonPassiveText.setText(""); this.pokemonPassiveText.setText("");
@ -4045,10 +4110,15 @@ export class StarterSelectUiHandler extends MessageUiHandler {
} }
const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId); const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId);
let eggMoves = 0;
if (species) {
const { starterDataEntry } = this.getSpeciesData(this.lastSpecies.speciesId);
eggMoves = starterDataEntry.eggMoves;
}
for (let em = 0; em < 4; em++) { for (let em = 0; em < 4; em++) {
const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null; const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null;
const eggMoveUnlocked = eggMove && globalScene.gameData.starterData[species.speciesId].eggMoves & (1 << em); const eggMoveUnlocked = eggMove && eggMoves & (1 << em);
this.pokemonEggMoveBgs[em].setFrame( this.pokemonEggMoveBgs[em].setFrame(
PokemonType[eggMove ? eggMove.type : PokemonType.UNKNOWN].toString().toLowerCase(), PokemonType[eggMove ? eggMove.type : PokemonType.UNKNOWN].toString().toLowerCase(),
); );
@ -4064,6 +4134,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.tryUpdateValue(); this.tryUpdateValue();
this.updateInstructions(); this.updateInstructions();
saveStarterPreferences(this.originalStarterPreferences);
} }
setTypeIcons(type1: PokemonType | null, type2: PokemonType | null): void { setTypeIcons(type1: PokemonType | null, type2: PokemonType | null): void {
@ -4157,9 +4229,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
textStyle = TextStyle.SUMMARY_GOLD; textStyle = TextStyle.SUMMARY_GOLD;
break; break;
} }
if (baseStarterValue - starterValue > 0) { starter.label.setColor(this.getTextColor(textStyle)).setShadowColor(this.getTextColor(textStyle, true));
starter.label.setColor(this.getTextColor(textStyle)).setShadowColor(this.getTextColor(textStyle, true));
}
} }
tryUpdateValue(add?: number, addingToParty?: boolean): boolean { tryUpdateValue(add?: number, addingToParty?: boolean): boolean {
@ -4318,14 +4388,12 @@ export class StarterSelectUiHandler extends MessageUiHandler {
originalStarterSelectCallback?.( originalStarterSelectCallback?.(
new Array(this.starterSpecies.length).fill(0).map((_, i) => { new Array(this.starterSpecies.length).fill(0).map((_, i) => {
const starterSpecies = thisObj.starterSpecies[i]; const starterSpecies = thisObj.starterSpecies[i];
const { starterDataEntry } = this.getSpeciesData(starterSpecies.speciesId);
return { return {
species: starterSpecies, species: starterSpecies,
dexAttr: thisObj.starterAttr[i], dexAttr: thisObj.starterAttr[i],
abilityIndex: thisObj.starterAbilityIndexes[i], abilityIndex: thisObj.starterAbilityIndexes[i],
passive: !( passive: !(starterDataEntry.passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)),
globalScene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^
(PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)
),
nature: thisObj.starterNatures[i] as Nature, nature: thisObj.starterNatures[i] as Nature,
teraType: thisObj.starterTeras[i] as PokemonType, teraType: thisObj.starterTeras[i] as PokemonType,
moveset: thisObj.starterMovesets[i], moveset: thisObj.starterMovesets[i],
@ -4382,7 +4450,8 @@ export class StarterSelectUiHandler extends MessageUiHandler {
*/ */
getCurrentDexProps(speciesId: number): bigint { getCurrentDexProps(speciesId: number): bigint {
let props = 0n; 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 /* 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 * 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
@ -4450,7 +4519,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr);
//@ts-expect-error //@ts-expect-error
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!?
this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id)); this.teraIcon.setVisible(this.allowTera);
const props = globalScene.gameData.getSpeciesDexAttrProps( const props = globalScene.gameData.getSpeciesDexAttrProps(
this.lastSpecies, this.lastSpecies,
this.getCurrentDexProps(this.lastSpecies.speciesId), this.getCurrentDexProps(this.lastSpecies.speciesId),
@ -4458,8 +4527,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
const formIndex = props.formIndex; const formIndex = props.formIndex;
this.canCycleTera = this.canCycleTera =
!this.statsMode && !this.statsMode &&
globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && this.allowTera &&
!isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2); !isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2) &&
!globalScene.gameMode.hasChallenge(Challenges.FRESH_START);
this.updateInstructions(); this.updateInstructions();
} }
} }
@ -4501,7 +4571,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
clear(): void { clear(): void {
super.clear(); super.clear();
saveStarterPreferences(this.starterPreferences); this.clearStarterPreferences();
this.cursor = -1; this.cursor = -1;
this.hideInstructions(); this.hideInstructions();
this.activeTooltip = undefined; this.activeTooltip = undefined;
@ -4544,5 +4614,6 @@ export class StarterSelectUiHandler extends MessageUiHandler {
*/ */
clearStarterPreferences() { clearStarterPreferences() {
this.starterPreferences = {}; this.starterPreferences = {};
this.originalStarterPreferences = {};
} }
} }

View File

@ -10,7 +10,8 @@ import type { MoveSourceType } from "#enums/move-source-type";
import type { SpeciesId } from "#enums/species-id"; import type { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import type { ModifierTypeOption } from "#modifiers/modifier-type"; 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 { BooleanHolder, type NumberHolder } from "./common";
import { getPokemonSpecies } from "./pokemon-utils"; import { getPokemonSpecies } from "./pokemon-utils";
@ -47,6 +48,20 @@ export function applyChallenges(
species: SpeciesId, species: SpeciesId,
cost: NumberHolder, cost: NumberHolder,
): boolean; ): boolean;
/**
* Apply all challenges that modify selectable starter data.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_SELECT_MODIFY
* @param speciesId {@link SpeciesId} The speciesId of the pokemon
* @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,
speciesId: SpeciesId,
dexEntry: DexEntry,
starterDataEntry: StarterDataEntry,
): boolean;
/** /**
* Apply all challenges that modify a starter after selection. * Apply all challenges that modify a starter after selection.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY * @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY
@ -269,6 +284,9 @@ export function applyChallenges(challengeType: ChallengeType, ...args: any[]): b
case ChallengeType.STARTER_COST: case ChallengeType.STARTER_COST:
ret ||= c.applyStarterCost(args[0], args[1]); ret ||= c.applyStarterCost(args[0], args[1]);
break; break;
case ChallengeType.STARTER_SELECT_MODIFY:
ret ||= c.applyStarterSelectModify(args[0], args[1], args[2]);
break;
case ChallengeType.STARTER_MODIFY: case ChallengeType.STARTER_MODIFY:
ret ||= c.applyStarterModify(args[0]); ret ||= c.applyStarterModify(args[0]);
break; break;