diff --git a/public/images/ui/summary_overlay_shiny.png b/public/images/ui/summary_overlay_shiny.png index 4102a45e3..e4ab82073 100644 Binary files a/public/images/ui/summary_overlay_shiny.png and b/public/images/ui/summary_overlay_shiny.png differ diff --git a/src/battle-phases.ts b/src/battle-phases.ts index bce987a6a..59e90e892 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -106,10 +106,12 @@ export class SelectStarterPhase extends BattlePhase { const party = this.scene.getParty(); const loadPokemonAssets: Promise[] = []; for (let starter of starters) { + const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr); const starterGender = starter.species.malePercent !== null - ? !starter.female ? Gender.MALE : Gender.FEMALE + ? !starterProps.female ? Gender.MALE : Gender.FEMALE : Gender.GENDERLESS; - const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starter.abilityIndex, starter.formIndex, starterGender, starter.shiny); + const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); + const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starterProps.abilityIndex, starterProps.formIndex, starterGender, starterProps.shiny, starterIvs); if (starter.pokerus) starterPokemon.pokerus = true; if (this.scene.gameMode === GameMode.SPLICED_ENDLESS) @@ -256,7 +258,8 @@ export class EncounterPhase extends BattlePhase { if (e < (battle.double ? 2 : 1)) { enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); enemyPokemon.resetSummonData(); - this.scene.gameData.setPokemonSeen(enemyPokemon); + if (!this.loaded) + this.scene.gameData.setPokemonSeen(enemyPokemon); } if (this.scene.gameMode === GameMode.CLASSIC && (battle.waveIndex === 200 || !(battle.waveIndex % 250)) && enemyPokemon.species.speciesId === Species.ETERNATUS) @@ -295,8 +298,10 @@ export class EncounterPhase extends BattlePhase { } this.scene.ui.setMode(Mode.MESSAGE).then(() => { - if (!this.loaded) + if (!this.loaded) { this.scene.gameData.saveSession(this.scene); + this.scene.gameData.saveSystem(); + } this.doEncounter(); }); }); @@ -2810,6 +2815,13 @@ export class AttemptCapturePhase extends PokemonPhase { if (pokemon.ivs.filter(iv => iv === 31).length === 6) this.scene.validateAchv(achvs.PERFECT_IVS); + + const dexEntry = this.scene.gameData.dexData[pokemon.species.speciesId]; + const dexIvs = dexEntry.ivs; + for (let i = 0; i < dexIvs.length; i++) { + if (dexIvs[i] < pokemon.ivs[i]) + dexIvs[i] = pokemon.ivs[i]; + } this.scene.ui.showText(`${pokemon.name} was caught!`, null, () => { const end = () => { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b170308f5..4adc19374 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1426,21 +1426,14 @@ export default class BattleScene extends Phaser.Scene { validateAchvs(achvType: { new(...args: any[]): Achv }, ...args: any[]): void { const filteredAchvs = Object.values(achvs).filter(a => a instanceof achvType); let newAchv = false; - for (let achv of filteredAchvs) { - if (this.validateAchv(achv, args, false)) - newAchv = true; - } - - if (newAchv) - this.gameData.saveSystem(); + for (let achv of filteredAchvs) + this.validateAchv(achv, args); } - validateAchv(achv: Achv, args?: any[], save: boolean = true): boolean { + validateAchv(achv: Achv, args?: any[]): boolean { if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) { this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.ui.achvBar.showAchv(achv); - if (save) - this.gameData.saveSystem(); return true; } diff --git a/src/debug.js b/src/debug.js index d409d7284..7a1725a51 100644 --- a/src/debug.js +++ b/src/debug.js @@ -2,7 +2,7 @@ function getData() { const dataStr = localStorage.getItem('data'); if (!dataStr) return null; - return JSON.parse(atob(dataStr)); + return JSON.parse(atob(dataStr), (k, v) => k.endsWith('Attr') ? BigInt(v) : v); } function getSession() { diff --git a/src/pokemon.ts b/src/pokemon.ts index 0f0bf2278..4ce9c8a6b 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -31,6 +31,7 @@ import SoundFade from 'phaser3-rex-plugins/plugins/soundfade'; import { GameMode } from './game-mode'; import { LevelMoves } from './data/pokemon-level-moves'; import { DamageAchv, achvs } from './system/achv'; +import { DexAttr } from './system/game-data'; export enum FieldPosition { CENTER, @@ -82,7 +83,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { private shinySparkle: Phaser.GameObjects.Sprite; - constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon | PokemonData) { + constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], dataSource?: Pokemon | PokemonData) { super(scene, x, y); if (!species.isObtainable() && this.isPlayer()) @@ -127,8 +128,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } else { this.generateAndPopulateMoveset(); - this.id = Utils.randInt(4294967295); - this.ivs = [ + this.id = Utils.randSeedInt(4294967295); + this.ivs = ivs || [ Utils.binToDec(Utils.decToBin(this.id).substring(0, 5)), Utils.binToDec(Utils.decToBin(this.id).substring(5, 10)), Utils.binToDec(Utils.decToBin(this.id).substring(10, 15)), @@ -221,6 +222,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return !this.isFainted() && !!this.scene && (!onField || this.isOnField()); } + getDexAttrs(): bigint { + let ret = 0n; + ret |= this.gender !== Gender.FEMALE ? DexAttr.MALE : DexAttr.FEMALE; + ret |= !this.shiny ? DexAttr.NON_SHINY : DexAttr.SHINY; + ret |= !this.abilityIndex ? DexAttr.ABILITY_1 : this.species.ability2 && this.abilityIndex === 1 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + ret |= this.scene.gameData.getFormAttr(this.formIndex); + return ret; + } + abstract isPlayer(): boolean; abstract hasTrainer(): boolean; @@ -413,7 +423,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { for (let s of stats) { const isHp = s === Stat.HP; let baseStat = baseStats[s]; - let value = Math.floor(((2 * baseStat + this.ivs[s] + (0 / 4)) * this.level) * 0.01); + let value = Math.floor(((2 * baseStat + this.ivs[s]) * this.level) * 0.01); if (isHp) { value = Math.min(value + this.level + 10, 99999); if (this.getAbility().hasAttr(NonSuperEffectiveImmunityAbAttr)) @@ -1352,8 +1362,8 @@ export class PlayerPokemon extends Pokemon { public metLevel: integer; public compatibleTms: Moves[]; - constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon | PokemonData) { - super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, dataSource); + constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], dataSource?: Pokemon | PokemonData) { + super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, ivs, dataSource); this.metBiome = scene.arena?.biomeType || Biome.TOWN; this.metLevel = level; @@ -1422,8 +1432,8 @@ export class PlayerPokemon extends Pokemon { this.getSpeciesForm().generateIconAnim(this.scene, this.gender === Gender.FEMALE, this.formIndex); this.compatibleTms.splice(0, this.compatibleTms.length); this.generateCompatibleTms(); - this.scene.gameData.setPokemonSeen(this); - this.scene.gameData.setPokemonCaught(this); + this.scene.gameData.setPokemonSeen(this, false); + this.scene.gameData.setPokemonCaught(this, false); this.loadAssets().then(() => { this.calculateStats(); this.updateInfo().then(() => resolve()); @@ -1508,7 +1518,7 @@ export class EnemyPokemon extends Pokemon { constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainer: boolean, dataSource?: PokemonData) { super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.getSpeciesFormIndex(species), - dataSource?.gender, dataSource?.shiny, dataSource); + dataSource?.gender, dataSource?.shiny, null, dataSource); this.trainer = trainer; @@ -1681,7 +1691,7 @@ export class EnemyPokemon extends Pokemon { let ret: PlayerPokemon = null; if (party.length < 6) { - const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this); + const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, null, this); party.push(newPokemon); ret = newPokemon; } diff --git a/src/system/auto-play.ts b/src/system/auto-play.ts index 4302a66df..aaf63ac81 100644 --- a/src/system/auto-play.ts +++ b/src/system/auto-play.ts @@ -3,7 +3,6 @@ import BattleScene, { Button } from "../battle-scene"; import { ModifierTier, ModifierType, ModifierTypeOption, PokemonBaseStatBoosterModifierType, PokemonHpRestoreModifierType, PokemonReviveModifierType } from "../modifier/modifier-type"; import Pokemon, { AiType, EnemyPokemon, PlayerPokemon, PokemonMove } from "../pokemon"; import { Species } from "../data/species"; -import { getTypeDamageMultiplier } from "../data/type"; import BattleMessageUiHandler from "../ui/battle-message-ui-handler"; import CommandUiHandler from "../ui/command-ui-handler"; import FightUiHandler from "../ui/fight-ui-handler"; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 8cb5e18b9..c9d13110a 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,5 +1,4 @@ import BattleScene, { PokeballCounts } from "../battle-scene"; -import { Gender } from "../data/gender"; import Pokemon, { EnemyPokemon, PlayerPokemon } from "../pokemon"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; import PokemonSpecies, { allSpecies, getPokemonSpecies } from "../data/pokemon-species"; @@ -51,29 +50,33 @@ interface AchvUnlocks { } export interface DexData { - [key: integer]: DexData | DexEntry + [key: integer]: DexEntry } export interface DexEntry { - seen: boolean; - caught: boolean; + seenAttr: bigint; + caughtAttr: bigint; + seenCount: integer; + caughtCount: integer; + ivs: integer[]; } -export interface DexEntryDetails { +export const DexAttr = { + NON_SHINY: 1n, + SHINY: 2n, + MALE: 4n, + FEMALE: 8n, + ABILITY_1: 16n, + ABILITY_2: 32n, + ABILITY_HIDDEN: 64n, + DEFAULT_FORM: 128n +} + +export interface DexAttrProps { shiny: boolean; - formIndex: integer; female: boolean; abilityIndex: integer; - entry: DexEntry; -} - -export interface StarterDexUnlockTree { - shiny: boolean | Map - formIndex: integer | Map - female: boolean | Map - abilityIndex: integer | Map - key: string, - entry: DexEntry + formIndex: integer; } export class GameData { @@ -106,8 +109,6 @@ export class GameData { public saveSystem(): boolean { if (this.scene.quickStart) return false; - - console.log(this.achvUnlocks, "wah") const data: SystemSaveData = { trainerId: this.trainerId, @@ -118,7 +119,10 @@ export class GameData { timestamp: new Date().getTime() }; - localStorage.setItem('data', btoa(JSON.stringify(data))); + localStorage.setItem('data_bak', localStorage.getItem('data')); + + const maxIntAttrValue = Math.pow(2, 31); + localStorage.setItem('data', btoa(JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v))); return true; } @@ -127,7 +131,8 @@ export class GameData { if (!localStorage.hasOwnProperty('data')) return false; - const data = JSON.parse(atob(localStorage.getItem('data'))) as SystemSaveData; + const data = JSON.parse(atob(localStorage.getItem('data')), (k: string, v: any) => k.endsWith('Attr') ? BigInt(v) : v) as SystemSaveData; + console.debug(data); this.trainerId = data.trainerId; @@ -147,10 +152,10 @@ export class GameData { } } - if (data.timestamp === undefined) - this.convertDexData(data.dexData); - - this.dexData = Object.assign(this.dexData, data.dexData); + if (data.dexData[1].hasOwnProperty(0)) + this.migrateLegacyDexData(this.dexData, data.dexData); + else + this.dexData = Object.assign(this.dexData, data.dexData); return true; } @@ -322,39 +327,10 @@ export class GameData { private initDexData(): void { const data: DexData = {}; - const initDexSubData = (dexData: DexData, count: integer): DexData[] => { - const ret: DexData[] = []; - for (let i = 0; i < count; i++) { - const newData: DexData = {}; - dexData[i] = newData; - ret.push(newData); - } - - return ret; - }; - - const initDexEntries = (dexData: DexData, count: integer): DexEntry[] => { - const ret: DexEntry[] = []; - for (let i = 0; i < count; i++) { - const entry: DexEntry = { seen: false, caught: false }; - dexData[i] = entry; - ret.push(entry); - } - - return ret; - }; - for (let species of allSpecies) { - data[species.speciesId] = {}; - const abilityCount = species.getAbilityCount(); - if (species.forms?.length) - initDexSubData(data[species.speciesId] as DexData, 2).map(sd => species.malePercent !== null - ? initDexSubData(sd, species.forms.length).map(fd => initDexSubData(fd, 2).map(gd => initDexEntries(gd, abilityCount))) - : initDexSubData(sd, species.forms.length).map(fd => initDexEntries(fd, abilityCount))); - else if (species.malePercent !== null) - initDexSubData(data[species.speciesId] as DexData, 2).map(sd => initDexSubData(sd, 2).map(gd => initDexEntries(gd, abilityCount))); - else - initDexSubData(data[species.speciesId] as DexData, 2).map(sd => initDexEntries(sd, abilityCount)) + data[species.speciesId] = { + seenAttr: 0n, caughtAttr: 0n, seenCount: 0, caughtCount: 0, ivs: [ 0, 0, 0, 0, 0, 0 ] + }; } const defaultStarters: Species[] = [ @@ -365,255 +341,196 @@ export class GameData { Species.SNIVY, Species.TEPIG, Species.OSHAWOTT ]; + const defaultStarterAttr = DexAttr.NON_SHINY | DexAttr.MALE | DexAttr.ABILITY_1 | DexAttr.DEFAULT_FORM; + for (let ds of defaultStarters) { - let entry = data[ds][0][Gender.MALE][0] as DexEntry; - entry.seen = true; - entry.caught = true; + let entry = data[ds] as DexEntry; + entry.seenAttr = defaultStarterAttr; + entry.caughtAttr = defaultStarterAttr; + for (let i in entry.ivs) + entry.ivs[i] = 10; } this.dexData = data; } - setPokemonSeen(pokemon: Pokemon): void { - const dexEntry = this.getPokemonDexEntry(pokemon); - if (!dexEntry.seen) { - dexEntry.seen = true; - this.saveSystem(); - } + setPokemonSeen(pokemon: Pokemon, incrementCount: boolean = true): void { + const dexEntry = this.dexData[pokemon.species.speciesId]; + dexEntry.seenAttr |= pokemon.getDexAttrs(); + if (incrementCount) + dexEntry.seenCount++; } - setPokemonCaught(pokemon: Pokemon): Promise { - return this.setPokemonSpeciesCaught(pokemon, pokemon.species); + setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true): Promise { + return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount); } - setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies): Promise { + setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount?: boolean): Promise { return new Promise((resolve) => { - const dexEntry = this.getDexEntry(species, pokemon.isShiny(), pokemon.formIndex, pokemon.gender === Gender.FEMALE, pokemon.abilityIndex); + const dexEntry = this.dexData[species.speciesId]; + const caughtAttr = dexEntry.caughtAttr; + dexEntry.caughtAttr |= pokemon.getDexAttrs(); + if (incrementCount) + dexEntry.seenCount++; + const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId); - if (!dexEntry.caught) { - const newCatch = !this.getDefaultDexEntry(species); + const newCatch = !caughtAttr; - dexEntry.caught = true; - this.saveSystem(); - - if (newCatch && !hasPrevolution) { - this.scene.playSoundWithoutBgm('level_up_fanfare', 1500); - this.scene.ui.showText(`${species.name} has been\nadded as a starter!`, null, () => resolve(), null, true); - return; - } + if (newCatch && !hasPrevolution) { + this.scene.playSoundWithoutBgm('level_up_fanfare', 1500); + this.scene.ui.showText(`${species.name} has been\nadded as a starter!`, null, () => resolve(), null, true); + return; } if (hasPrevolution) { const prevolutionSpecies = pokemonPrevolutions[species.speciesId]; - this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies)).then(() => resolve()); + return this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies)).then(() => resolve()); } else resolve(); }); } - getPokemonDexEntry(pokemon: Pokemon) { - return this.getDexEntry(pokemon.species, pokemon.isShiny(), pokemon.formIndex, pokemon.gender === Gender.FEMALE, pokemon.abilityIndex); + getSpeciesDefaultDexAttr(species: PokemonSpecies): bigint { + let ret = 0n; + const dexEntry = this.dexData[species.speciesId]; + const attr = dexEntry.caughtAttr; + ret |= attr & DexAttr.NON_SHINY || !(attr & DexAttr.SHINY) ? DexAttr.NON_SHINY : DexAttr.SHINY; + ret |= attr & DexAttr.MALE || !(attr & DexAttr.FEMALE) ? DexAttr.MALE : DexAttr.FEMALE; + ret |= attr & DexAttr.ABILITY_1 || (!(attr & DexAttr.ABILITY_2) && !(attr & DexAttr.ABILITY_HIDDEN)) ? DexAttr.ABILITY_1 : attr & DexAttr.ABILITY_2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + ret |= this.getFormAttr(this.getFormIndex(attr)); + return ret; } - getDexEntry(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer): DexEntry { - const shinyIndex = !shiny ? 0 : 1; - const genderIndex = !female ? 0 : 1; - const data = this.dexData[species.speciesId]; - if (species.forms?.length) { - const getEntry = () => { - if (species.malePercent !== null) - return data[shinyIndex][formIndex][genderIndex][abilityIndex]; - return data[shinyIndex][formIndex][abilityIndex]; - }; - let entry: DexEntry; - try { - entry = getEntry(); - } catch (err) { } - if (entry) - return entry; - else { - console.warn(`Form data not found for dex entry for ${species.name}: Restructuring dex entry`); - for (let s = 0; s < 2; s++) { - const oldData = Object.assign({}, data[s]); - data[s] = {}; - for (let f = 0; f < species.forms.length; f++) - data[s][f] = oldData; - } - this.saveSystem(); - } - return getEntry(); - } else if (species.malePercent !== null) - return data[shinyIndex][genderIndex][abilityIndex]; - return data[shinyIndex][abilityIndex] as DexEntry; - } + getSpeciesDexAttrProps(species: PokemonSpecies, dexAttr: bigint): DexAttrProps { + const shiny = !(dexAttr & DexAttr.NON_SHINY); + const female = !(dexAttr & DexAttr.MALE); + const abilityIndex = dexAttr & DexAttr.ABILITY_1 ? 0 : !species.ability2 || dexAttr & DexAttr.ABILITY_2 ? 1 : 2; + const formIndex = this.getFormIndex(dexAttr); - getDefaultDexEntry(species: PokemonSpecies, forceShiny?: boolean, forceFormIndex?: integer, forceFemale?: boolean, forceAbilityIndex?: integer): DexEntryDetails { - const hasForms = !!species.forms?.length; - const hasGender = species.malePercent !== null; - let shiny = false; - let formIndex = 0; - let female = false; - let abilityIndex = 0; - let entry = null; - - const traverseData = (data: DexData, level: integer) => { - const keys = Object.keys(data); - if ((!hasForms && level === 1) || (!hasGender && level === 2)) { - traverseData(data, level + 1); - return; - } - keys.forEach((key: string, k: integer) => { - if (entry) - return; - - switch (level) { - case 0: - shiny = !!k; - if (forceShiny !== undefined && shiny !== forceShiny) - return; - break; - case 1: - formIndex = k; - if (forceFormIndex !== undefined && formIndex !== forceFormIndex) - return; - break; - case 2: - female = !!k; - if (forceFemale !== undefined && female !== forceFemale) - return - break; - case 3: - abilityIndex = k; - if (forceAbilityIndex !== undefined && abilityIndex !== forceAbilityIndex) - return; - break; - } - - if ('caught' in data[key]) { - if (data[key].caught) - entry = data[key] as DexEntry; - } else - traverseData(data[key] as DexData, level + 1); - }); + return { + shiny, + female, + abilityIndex, + formIndex }; + } - traverseData(this.dexData[species.speciesId] as DexData, 0); + getFormIndex(attr: bigint): integer { + if (!attr || attr < DexAttr.DEFAULT_FORM) + return 0; + let f = 0; + while (!(attr & this.getFormAttr(f))) + f++; + return f; + } - if (entry) { - return { - shiny: shiny, - formIndex: formIndex, - female: female, - abilityIndex: abilityIndex, - entry: entry - }; + getFormAttr(formIndex: integer): bigint { + return BigInt(Math.pow(2, 7 + formIndex)); + } + + // TODO: Remove + migrateLegacyDexData(dexData: DexData, legacyDexData: object): DexData { + const newDexData: DexData = {}; + + for (let s of Object.keys(legacyDexData)) { + const species = getPokemonSpecies(parseInt(s)); + const newEntry = dexData[parseInt(s)]; + let seenAttr = 0n; + let caughtAttr = 0n; + Object.keys(legacyDexData[s]).forEach(shinyIndex => { + const shinyData = legacyDexData[s][shinyIndex]; + if (species.forms?.length) { + Object.keys(shinyData).forEach(formIndex => { + const formData = shinyData[formIndex]; + if (species.malePercent !== null) { + Object.keys(formData).forEach(genderIndex => { + const genderData = formData[genderIndex]; + Object.keys(genderData).forEach(abilityIndex => { + const entry = genderData[abilityIndex]; + if (entry.seen) { + seenAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + seenAttr |= !parseInt(genderIndex) ? DexAttr.MALE : DexAttr.FEMALE; + seenAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + seenAttr |= this.getFormAttr(parseInt(formIndex)); + } + if (entry.caught) { + if (!caughtAttr) + newEntry.ivs = [ 10, 10, 10, 10, 10, 10 ]; + caughtAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + caughtAttr |= !parseInt(genderIndex) ? DexAttr.MALE : DexAttr.FEMALE; + caughtAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + caughtAttr |= this.getFormAttr(parseInt(formIndex)); + } + }); + }); + } else { + Object.keys(formData).forEach(abilityIndex => { + const entry = formData[abilityIndex]; + if (entry.seen) { + seenAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + seenAttr |= DexAttr.MALE; + seenAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + seenAttr |= this.getFormAttr(parseInt(formIndex)); + } + if (entry.caught) { + if (!caughtAttr) + newEntry.ivs = [ 10, 10, 10, 10, 10, 10 ]; + caughtAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + caughtAttr |= DexAttr.MALE; + caughtAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + caughtAttr |= this.getFormAttr(parseInt(formIndex)); + } + }); + } + }); + } else { + if (species.malePercent !== null) { + Object.keys(shinyData).forEach(genderIndex => { + const genderData = shinyData[genderIndex]; + Object.keys(genderData).forEach(abilityIndex => { + const entry = genderData[abilityIndex]; + if (entry.seen) { + seenAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + seenAttr |= !parseInt(genderIndex) ? DexAttr.MALE : DexAttr.FEMALE; + seenAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + seenAttr |= DexAttr.DEFAULT_FORM; + } + if (entry.caught) { + if (!caughtAttr) + newEntry.ivs = [ 10, 10, 10, 10, 10, 10 ]; + caughtAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + caughtAttr |= !parseInt(genderIndex) ? DexAttr.MALE : DexAttr.FEMALE; + caughtAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + caughtAttr |= DexAttr.DEFAULT_FORM; + } + }); + }); + } else { + Object.keys(shinyData).forEach(abilityIndex => { + const entry = shinyData[abilityIndex]; + if (entry.seen) { + seenAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + seenAttr |= DexAttr.MALE; + seenAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + seenAttr |= DexAttr.DEFAULT_FORM; + } + if (entry.caught) { + if (!caughtAttr) + newEntry.ivs = [ 10, 10, 10, 10, 10, 10 ]; + caughtAttr |= !parseInt(shinyIndex) ? DexAttr.NON_SHINY : DexAttr.SHINY; + caughtAttr |= DexAttr.MALE; + caughtAttr |= parseInt(abilityIndex) === 0 ? DexAttr.ABILITY_1 : parseInt(abilityIndex) === 1 && species.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + caughtAttr |= DexAttr.DEFAULT_FORM; + } + }); + } + } + }); + + newEntry.seenAttr = seenAttr; + newEntry.caughtAttr = caughtAttr; } - return null; - } - - getStarterDexUnlockTree(species: PokemonSpecies): StarterDexUnlockTree { - const hasForms = !!species.forms?.length; - const hasGender = species.malePercent !== null; - - const getTreeOrValueMap = (key: string, parent?: StarterDexUnlockTree): (Map) => { - switch (key) { - case 'shiny': - const shinyMap = new Map(); - for (let s = 0; s < 2; s++) { - const props = { shiny: !!s }; - shinyMap.set(!!s, { - shiny: !!s, - formIndex: hasForms ? getTreeOrValueMap('formIndex', props as StarterDexUnlockTree) : null, - female: !hasForms && hasGender ? getTreeOrValueMap('female', props as StarterDexUnlockTree) : null, - abilityIndex: !hasForms && !hasGender ? getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree) : null, - key: hasForms ? 'formIndex' : hasGender ? 'female' : 'abilityIndex', - entry: null, - }); - } - return shinyMap; - case 'formIndex': - const formMap = new Map(); - for (let f = 0; f < species.forms.length; f++) { - const props = { shiny: parent.shiny, formIndex: f }; - formMap.set(f, { - shiny: parent.shiny, - formIndex: f, - female: hasGender ? getTreeOrValueMap('female', props as StarterDexUnlockTree) : null, - abilityIndex: !hasGender ? getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree) : null, - key: hasGender ? 'female' : 'abilityIndex', - entry: null - }); - } - return formMap; - case 'female': - const genderMap = new Map(); - for (let g = 0; g < 2; g++) { - const props = { shiny: parent.shiny, formIndex: parent.formIndex, female: !!g }; - genderMap.set(!!g, { - shiny: parent.shiny, - formIndex: parent.formIndex, - female: !!g, - abilityIndex: getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree), - key: 'abilityIndex', - entry: null - }); - } - return genderMap; - case 'abilityIndex': - const abilityMap = new Map(); - const abilityCount = species.getAbilityCount(); - for (let a = 0; a < abilityCount; a++) { - abilityMap.set(a, { - shiny: parent.shiny, - formIndex: parent.formIndex, - female: parent.female, - abilityIndex: a, - key: 'entry', - entry: hasForms - ? hasGender - ? this.dexData[species.speciesId][!parent.shiny ? 0 : 1][parent.formIndex as integer][!parent.female ? 0 : 1][a] - : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][parent.formIndex as integer][a] - : hasGender - ? this.dexData[species.speciesId][!parent.shiny ? 0 : 1][!parent.female ? 0 : 1][a] - : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][a] - }); - } - return abilityMap; - } - }; - - const root = { - shiny: getTreeOrValueMap('shiny'), - formIndex: null, - female: null, - abilityIndex: null, - key: 'shiny', - entry: null - }; - - return root; - } - - convertDexData(dexData: DexData): void { - const traverseData = (speciesId: Species, data: DexData) => { - const keys = Object.keys(data); - keys.forEach((key: string, k: integer) => { - if ('caught' in data[key]) { - const abilityCount = getPokemonSpecies(speciesId).getAbilityCount(); - data[key] = { - 0: data[key] - }; - for (let a = 1; a < abilityCount; a++) - data[key][a] = { seen: false, caught: false }; - } else - traverseData(speciesId, data[key]); - }); - } - - Object.keys(dexData).forEach((species: string, s: integer) => { - const speciesId = parseInt(species); - traverseData(speciesId, dexData[species]); - }); + return newDexData; } } \ No newline at end of file diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 4ce2caa98..02de841ee 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -85,7 +85,7 @@ export default class PokemonData { toPokemon(scene: BattleScene, battleType?: BattleType): Pokemon { const species = getPokemonSpecies(this.species); if (this.player) - return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this); + return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, null, this); return new EnemyPokemon(scene, species, this.level, battleType === BattleType.TRAINER, this); } } \ No newline at end of file diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 5a32b5708..3f4e3ed2a 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -129,9 +129,9 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } if (!this.player) { - const speciesOwned = !!pokemon.scene.gameData.getDefaultDexEntry(pokemon.species)?.entry?.caught; - this.ownedIcon.setVisible(speciesOwned); - if (!pokemon.scene.gameData.getPokemonDexEntry(pokemon)?.caught) + const dexEntry = pokemon.scene.gameData.dexData[pokemon.species.speciesId]; + this.ownedIcon.setVisible(!!dexEntry.caughtAttr); + if (!(dexEntry.caughtAttr & pokemon.getDexAttrs())) this.ownedIcon.setTint(0x808080); } diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 51ba541ce..56576f719 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -186,7 +186,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { getIvDescriptor(value: integer): string { if (value > 30) - return 'Perfect'; + return 'Best'; if (value === 30) return 'Fantastic'; if (value > 20) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 7fe60d8ad..7d34ecf0c 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -5,22 +5,19 @@ import { TextStyle, addTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; import MessageUiHandler from "./message-ui-handler"; -import { DexEntryDetails, StarterDexUnlockTree } from "../system/game-data"; import { Gender, getGenderColor, getGenderSymbol } from "../data/gender"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; import { abilities } from "../data/ability"; import { GameMode } from "../game-mode"; import { Unlockables } from "../system/unlockables"; import { GrowthRate, getGrowthRateColor } from "../data/exp"; +import { DexAttr, DexEntry } from "../system/game-data"; export type StarterSelectCallback = (starters: Starter[]) => void; export interface Starter { species: PokemonSpecies; - shiny: boolean; - formIndex: integer; - female: boolean; - abilityIndex: integer; + dexAttr: bigint; pokerus: boolean; } @@ -39,10 +36,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; private genMode: boolean; - private shinyCursor: integer = 0; - private formCursor: integer = 0; - private genderCursor: integer = 0; - private abilityCursor: integer = 0; + private dexAttrCursor: bigint = 0n; private genCursor: integer = 0; private genSpecies: PokemonSpecies[][] = []; @@ -52,9 +46,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterCursors: integer[] = []; private pokerusGens: integer[] = []; private pokerusCursors: integer[] = []; - private starterDetails: [boolean, integer, boolean, integer][] = []; - private speciesStarterDexEntry: DexEntryDetails; - private speciesStarterDexTree: StarterDexUnlockTree; + private starterAttr: bigint[] = []; + private speciesStarterDexEntry: DexEntry; private canCycleShiny: boolean; private canCycleForm: boolean; private canCycleGender: boolean; @@ -175,14 +168,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { starterSpecies.push(species.speciesId); this.speciesLoaded.set(species.speciesId, false); this.genSpecies[g].push(species); - const dexEntry = this.scene.gameData.getDefaultDexEntry(species); - species.generateIconAnim(this.scene, dexEntry?.female, dexEntry?.formIndex); + const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species); + const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); + species.generateIconAnim(this.scene, defaultProps.female, defaultProps.formIndex); const x = (s % 9) * 18; const y = Math.floor(s / 9) * 18; - const icon = this.scene.add.sprite(x, y, species.getIconAtlasKey(dexEntry?.formIndex)); + const icon = this.scene.add.sprite(x, y, species.getIconAtlasKey(defaultProps.formIndex)); icon.setScale(0.5); icon.setOrigin(0, 0); - icon.play(species.getIconKey(dexEntry?.female, dexEntry?.formIndex)).stop(); + icon.play(species.getIconKey(defaultProps.female, defaultProps.formIndex)).stop(); icon.setTintFill(0); this.starterSelectGenIconContainers[g].add(icon); s++; @@ -277,9 +271,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let g = 0; g < this.genSpecies.length; g++) { this.genSpecies[g].forEach((species, s) => { - const dexEntry = this.scene.gameData.getDefaultDexEntry(species); + const dexEntry = this.scene.gameData.dexData[species.speciesId]; const icon = this.starterSelectGenIconContainers[g].getAt(s) as Phaser.GameObjects.Sprite; - if (dexEntry) + if (dexEntry.caughtAttr) icon.clearTint(); }); } @@ -323,7 +317,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } else { if (button === Button.ACTION) { - if (!this.speciesStarterDexEntry) + if (!this.speciesStarterDexEntry?.caughtAttr) error = true; else if (this.starterCursors.length < 3) { let isDupe = false; @@ -338,10 +332,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { cursorObj.setVisible(true); cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); const species = this.genSpecies[this.genCursor][this.cursor]; - this.starterIcons[this.starterCursors.length].play(species.getIconKey(this.speciesStarterDexEntry?.female)); + const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species); + const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); + this.starterIcons[this.starterCursors.length].play(species.getIconKey(defaultProps.female, defaultProps.formIndex)); this.starterGens.push(this.genCursor); this.starterCursors.push(this.cursor); - this.starterDetails.push([ !!this.shinyCursor, this.formCursor, !!this.genderCursor, this.abilityCursor ]); + this.starterAttr.push(this.dexAttrCursor); if (this.speciesLoaded.get(species.speciesId)) species.cry(this.scene); if (this.starterCursors.length === 3) { @@ -362,10 +358,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const starterSpecies = thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]]; return { species: starterSpecies, - shiny: thisObj.starterDetails[i][0], - formIndex: thisObj.starterDetails[i][1], - female: thisObj.starterDetails[i][2], - abilityIndex: thisObj.starterDetails[i][3], + dexAttr: thisObj.starterAttr[i], pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length }; })); @@ -394,11 +387,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const genStarters = this.starterSelectGenIconContainers[this.genCursor].getAll().length; const rows = Math.ceil(genStarters / 9); const row = Math.floor(this.cursor / 9); + const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor); switch (button) { case Button.CYCLE_SHINY: if (this.canCycleShiny) { - this.setSpeciesDetails(this.lastSpecies, !this.shinyCursor, undefined, undefined, undefined); - if (this.shinyCursor) + this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, undefined); + if (this.dexAttrCursor & DexAttr.SHINY) this.scene.playSound('sparkle'); else success = true; @@ -406,20 +400,41 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; case Button.CYCLE_FORM: if (this.canCycleForm) { - this.setSpeciesDetails(this.lastSpecies, undefined, (this.formCursor + 1) % this.lastSpecies.forms.length, undefined, undefined); + const formCount = this.lastSpecies.forms.length; + let newFormIndex = props.formIndex; + do { + newFormIndex = (newFormIndex + 1) % formCount; + if (this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) + break; + } while (newFormIndex !== props.formIndex); + this.setSpeciesDetails(this.lastSpecies, undefined, newFormIndex, undefined, undefined); success = true; } break; case Button.CYCLE_GENDER: if (this.canCycleGender) { - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !this.genderCursor, undefined); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !props.female, undefined); success = true; } break; case Button.CYCLE_ABILITY: if (this.canCycleAbility) { const abilityCount = this.lastSpecies.getAbilityCount(); - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, (this.abilityCursor + 1) % abilityCount); + let newAbilityIndex = props.abilityIndex; + do { + newAbilityIndex = (newAbilityIndex + 1) % abilityCount; + if (!newAbilityIndex) { + if (this.speciesStarterDexEntry.caughtAttr & DexAttr.ABILITY_1) + break; + } else if (newAbilityIndex === 1) { + if (this.speciesStarterDexEntry.caughtAttr & (this.lastSpecies.ability2 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN)) + break; + } else { + if (this.speciesStarterDexEntry.caughtAttr & DexAttr.ABILITY_HIDDEN) + break; + } + } while (newAbilityIndex !== props.abilityIndex); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newAbilityIndex); success = true; } break; @@ -462,7 +477,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { instructionLines.push('A/Space/Enter: Select'); if (this.starterCursors.length) instructionLines.push('X/Backspace/Esc: Undo'); - if (this.speciesStarterDexTree) { + if (this.speciesStarterDexEntry?.caughtAttr) { if (this.canCycleShiny) cycleInstructionLines.push('R: Cycle Shiny'); if (this.canCycleForm) @@ -505,9 +520,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokerusCursorObjs[s].setVisible(this.pokerusGens[s] === cursor); const genLimit = this.genSpecies[this.genCursor].length; - for (let s = 0; s < 81; s++) { - this.shinyIcons[s].setVisible(s < genLimit && !!this.scene.gameData.getDefaultDexEntry(this.genSpecies[this.genCursor][s], true)); - } + for (let s = 0; s < 81; s++) + this.shinyIcons[s].setVisible(s < genLimit && !!(this.scene.gameData.dexData[this.genSpecies[this.genCursor][s].speciesId].caughtAttr & DexAttr.SHINY)); } else { changed = super.setCursor(cursor); @@ -539,18 +553,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } setSpecies(species: PokemonSpecies) { - this.speciesStarterDexEntry = species ? this.scene.gameData.getDefaultDexEntry(species) : null; - this.speciesStarterDexTree = this.speciesStarterDexEntry ? this.scene.gameData.getStarterDexUnlockTree(species) : null; + this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; + this.dexAttrCursor = species ? this.scene.gameData.getSpeciesDefaultDexAttr(species) : 0n; if (this.lastSpecies) { - const defaultStarterDexEntry = this.scene.gameData.getDefaultDexEntry(this.lastSpecies); + const dexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(this.lastSpecies); + const props = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, dexAttr); const lastSpeciesIcon = (this.starterSelectGenIconContainers[this.lastSpecies.generation - 1].getAt(this.genSpecies[this.lastSpecies.generation - 1].indexOf(this.lastSpecies)) as Phaser.GameObjects.Sprite); - lastSpeciesIcon.play(this.lastSpecies.getIconKey(!!defaultStarterDexEntry?.female, defaultStarterDexEntry?.formIndex)).stop(); + lastSpeciesIcon.play(this.lastSpecies.getIconKey(props.female, props.formIndex)).stop(); } this.lastSpecies = species; - if (species && this.speciesStarterDexEntry) { + if (species && this.speciesStarterDexEntry?.caughtAttr) { this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 3)); this.pokemonNameText.setText(species.name); this.pokemonGrowthRateText.setText(Utils.toReadableString(GrowthRate[species.growthRate])); @@ -558,8 +573,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true)); this.pokemonGrowthRateLabelText.setVisible(true); this.pokemonAbilityLabelText.setVisible(true); + + const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species); + const props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); - this.setSpeciesDetails(species, !!this.speciesStarterDexEntry?.shiny, this.speciesStarterDexEntry?.formIndex, !!this.speciesStarterDexEntry?.female, this.speciesStarterDexEntry?.abilityIndex); + this.setSpeciesDetails(species, props.shiny, props.formIndex, props.female, props.abilityIndex); } else { this.pokemonNumberText.setText(Utils.padInt(0, 3)); this.pokemonNameText.setText(species ? '???' : ''); @@ -572,14 +590,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer): void { - if (shiny !== undefined) - this.shinyCursor = !shiny ? 0 : 1; - if (formIndex !== undefined) - this.formCursor = formIndex; - if (female !== undefined) - this.genderCursor = !female ? 0 : 1; - if (abilityIndex !== undefined) - this.abilityCursor = abilityIndex; + const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null; + this.dexAttrCursor = 0n; + + if (species) { + this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; + this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE; + this.dexAttrCursor |= (abilityIndex !== undefined ? !abilityIndex : !(abilityIndex = oldProps.abilityIndex)) ? DexAttr.ABILITY_1 : species.ability2 && abilityIndex === 1 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; + this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps.formIndex)); + } this.pokemonSprite.setVisible(false); @@ -589,26 +608,20 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (species) { - const defaultDexEntry = this.scene.gameData.getDefaultDexEntry(species, shiny, formIndex, female, abilityIndex) || this.scene.gameData.getDefaultDexEntry(species); - const dexEntry = this.scene.gameData.getDexEntry(species, !!this.shinyCursor, this.formCursor, !!this.genderCursor, this.abilityCursor); - - if (!dexEntry.caught) { - if (shiny === undefined || (defaultDexEntry && shiny !== defaultDexEntry.shiny)) - shiny = defaultDexEntry.shiny; - if (formIndex === undefined || (defaultDexEntry && formIndex !== defaultDexEntry.formIndex)) - formIndex = defaultDexEntry.formIndex || 0; - if (female === undefined || (defaultDexEntry && female !== defaultDexEntry.female)) - female = defaultDexEntry.female; - if (abilityIndex === undefined || (defaultDexEntry && abilityIndex !== defaultDexEntry.abilityIndex)) - abilityIndex = defaultDexEntry.abilityIndex; - } else { - shiny = !!this.shinyCursor; - formIndex = this.formCursor; - female = !!this.genderCursor; - abilityIndex = this.abilityCursor; + const dexEntry = this.scene.gameData.dexData[species.speciesId]; + if (!dexEntry.caughtAttr) { + const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species)); + if (shiny === undefined || shiny !== props.shiny) + shiny = props.shiny; + if (formIndex === undefined || formIndex !== props.formIndex) + formIndex = props.formIndex; + if (female === undefined || female !== props.female) + female = props.female; + if (abilityIndex === undefined || abilityIndex !== props.abilityIndex) + abilityIndex = props.abilityIndex; } - if (this.speciesStarterDexTree) { + if (this.speciesStarterDexEntry?.caughtAttr) { const assetLoadCancelled = new Utils.BooleanHolder(false); this.assetLoadCancelled = assetLoadCancelled; @@ -624,53 +637,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { species.generateIconAnim(this.scene, female, formIndex); (this.starterSelectGenIconContainers[this.genCursor].getAt(this.cursor) as Phaser.GameObjects.Sprite).play(species.getIconKey(female, formIndex)); - let count: integer; - let values: any[]; - const calcUnlockedCount = (tree: StarterDexUnlockTree, prop: string, root?: boolean) => { - if (root) { - count = 0; - values = []; - } - if (!tree.entry) { - if (!tree[tree.key]) - console.log(tree, tree.key); - for (let key of tree[tree.key].keys()) - calcUnlockedCount(tree[tree.key].get(key), prop); - } else if (tree.entry.caught) { - if (values.indexOf(tree[prop]) === -1) { - values.push(tree[prop]); - count++; - } - } - }; - - let tree = this.speciesStarterDexTree; - - calcUnlockedCount(tree, 'shiny', true); - this.canCycleShiny = count > 1; - tree = (tree.shiny as Map).get(!!this.shinyCursor); - - if (this.lastSpecies.forms?.length) { - calcUnlockedCount(tree, 'formIndex', true); - this.canCycleForm = count > 1; - tree = (tree.formIndex as Map).get(this.formCursor); - } else - this.canCycleForm = false; - - if (this.lastSpecies.malePercent !== null) { - calcUnlockedCount(tree, 'female', true); - this.canCycleGender = count > 1; - } else - this.canCycleGender = false; - - if (this.lastSpecies.getAbilityCount() > 1) { - calcUnlockedCount(tree, 'abilityIndex', true); - this.canCycleAbility = count > 1; - } else - this.canCycleAbility = false; + this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); + this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); + this.canCycleAbility = [ dexEntry.caughtAttr & DexAttr.ABILITY_1, dexEntry.caughtAttr & DexAttr.ABILITY_2, dexEntry.caughtAttr & DexAttr.ABILITY_HIDDEN ].filter(a => a).length > 1; + this.canCycleForm = species.forms.map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(a => a).length > 1; } - if (defaultDexEntry && species.malePercent !== null) { + if (dexEntry.caughtAttr && species.malePercent !== null) { const gender = !female ? Gender.MALE : Gender.FEMALE; this.pokemonGenderText.setText(getGenderSymbol(gender)); this.pokemonGenderText.setColor(getGenderColor(gender)); @@ -678,7 +651,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else this.pokemonGenderText.setText(''); - if (defaultDexEntry) { + if (dexEntry.caughtAttr) { const ability = this.lastSpecies.getAbility(abilityIndex); this.pokemonAbilityText.setText(abilities[ability].name); @@ -698,7 +671,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { popStarter(): void { this.starterGens.pop(); this.starterCursors.pop(); - this.starterDetails.pop(); + this.starterAttr.pop(); this.starterCursorObjs[this.starterCursors.length].setVisible(false); this.starterIcons[this.starterCursors.length].play('pkmn_icon__000'); } diff --git a/src/utils.ts b/src/utils.ts index a97f8c397..86a16d9e7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -81,8 +81,8 @@ export function getFrameMs(frameCount: integer): integer { } export function binToDec(input: string): integer { - let place:integer[] = []; - let binary:string[] = []; + let place: integer[] = []; + let binary: string[] = []; let decimalNum = 0;