2023-04-28 12:03:42 -07:00
import BattleScene , { PokeballCounts } from "../battle-scene" ;
import Pokemon , { EnemyPokemon , PlayerPokemon } from "../pokemon" ;
2023-04-20 12:46:05 -07:00
import { pokemonPrevolutions } from "../data/pokemon-evolutions" ;
2023-12-01 14:23:26 -08:00
import PokemonSpecies , { allSpecies , getPokemonSpecies , speciesStarters } from "../data/pokemon-species" ;
2023-04-20 12:46:05 -07:00
import { Species } from "../data/species" ;
import * as Utils from "../utils" ;
2023-04-28 12:03:42 -07:00
import PokemonData from "./pokemon-data" ;
import PersistentModifierData from "./modifier-data" ;
import ArenaData from "./arena-data" ;
2023-04-28 22:40:24 -07:00
import { Unlockables } from "./unlockables" ;
2023-05-31 16:54:57 -07:00
import { GameMode } from "../game-mode" ;
2023-10-07 13:08:33 -07:00
import { BattleType } from "../battle" ;
import TrainerData from "./trainer-data" ;
import { trainerConfigs } from "../data/trainer-type" ;
2023-10-26 13:33:59 -07:00
import { Setting , setSetting , settingDefaults } from "./settings" ;
2023-11-11 21:31:40 -08:00
import { achvs } from "./achv" ;
2023-12-15 20:07:32 -08:00
import EggData from "./egg-data" ;
import { Egg } from "../data/egg" ;
2023-12-19 20:51:48 -08:00
import { VoucherType , vouchers } from "./voucher" ;
2023-12-26 11:49:23 -08:00
import { AES , enc } from "crypto-js" ;
import { Mode } from "../ui/ui" ;
const saveKey = 'x0i2O7WRiANTqPmZ' ; // Temporary; secure encryption is not yet necessary
export enum GameDataType {
SYSTEM ,
SESSION ,
SETTINGS
}
export function getDataTypeKey ( dataType : GameDataType ) : string {
switch ( dataType ) {
case GameDataType . SYSTEM :
return 'data' ;
case GameDataType . SESSION :
return 'sessionData' ;
case GameDataType . SETTINGS :
return 'settings' ;
}
}
2023-04-17 19:44:41 -07:00
2023-04-28 12:03:42 -07:00
interface SystemSaveData {
2023-04-17 22:32:26 -07:00
trainerId : integer ;
secretId : integer ;
dexData : DexData ;
2023-04-28 22:40:24 -07:00
unlocks : Unlocks ;
2023-11-11 21:31:40 -08:00
achvUnlocks : AchvUnlocks ;
2023-12-19 20:51:48 -08:00
voucherUnlocks : VoucherUnlocks ;
voucherCounts : VoucherCounts ;
2023-12-15 20:07:32 -08:00
eggs : EggData [ ] ;
2023-12-13 21:41:35 -08:00
gameVersion : string ;
2023-04-28 12:03:42 -07:00
timestamp : integer ;
}
interface SessionSaveData {
2023-10-18 15:01:15 -07:00
seed : string ;
2023-05-31 16:54:57 -07:00
gameMode : GameMode ;
2023-04-28 12:03:42 -07:00
party : PokemonData [ ] ;
2023-10-07 13:08:33 -07:00
enemyParty : PokemonData [ ] ;
2023-05-18 08:11:06 -07:00
enemyField : PokemonData [ ] ;
2023-04-28 12:03:42 -07:00
modifiers : PersistentModifierData [ ] ;
enemyModifiers : PersistentModifierData [ ] ;
arena : ArenaData ;
pokeballCounts : PokeballCounts ;
2023-11-10 12:51:34 -08:00
money : integer ;
2023-04-28 12:03:42 -07:00
waveIndex : integer ;
2023-10-07 13:08:33 -07:00
battleType : BattleType ;
trainer : TrainerData ;
2023-12-13 21:41:35 -08:00
gameVersion : string ;
2023-04-28 12:03:42 -07:00
timestamp : integer ;
2023-04-17 22:32:26 -07:00
}
2023-04-28 22:40:24 -07:00
interface Unlocks {
[ key : integer ] : boolean ;
}
2023-11-11 21:31:40 -08:00
interface AchvUnlocks {
[ key : string ] : integer
}
2023-12-19 20:51:48 -08:00
interface VoucherUnlocks {
[ key : string ] : integer
}
export interface VoucherCounts {
[ type : string ] : integer ;
}
2023-04-17 19:44:41 -07:00
export interface DexData {
2023-11-12 20:47:04 -08:00
[ key : integer ] : DexEntry
2023-04-17 19:44:41 -07:00
}
export interface DexEntry {
2023-11-12 20:47:04 -08:00
seenAttr : bigint ;
caughtAttr : bigint ;
seenCount : integer ;
caughtCount : integer ;
ivs : integer [ ] ;
2023-04-17 19:44:41 -07:00
}
2023-11-12 20:47:04 -08:00
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 {
2023-04-17 19:44:41 -07:00
shiny : boolean ;
female : boolean ;
2023-04-26 09:50:21 -07:00
abilityIndex : integer ;
2023-11-12 20:47:04 -08:00
formIndex : integer ;
2023-04-17 19:44:41 -07:00
}
2023-04-17 22:32:26 -07:00
export class GameData {
2023-04-17 19:44:41 -07:00
private scene : BattleScene ;
public trainerId : integer ;
public secretId : integer ;
public dexData : DexData ;
2023-04-28 22:40:24 -07:00
public unlocks : Unlocks ;
2023-11-11 21:31:40 -08:00
public achvUnlocks : AchvUnlocks ;
2023-12-19 20:51:48 -08:00
public voucherUnlocks : VoucherUnlocks ;
public voucherCounts : VoucherCounts ;
2023-12-15 20:07:32 -08:00
public eggs : Egg [ ] ;
2023-04-17 19:44:41 -07:00
constructor ( scene : BattleScene ) {
this . scene = scene ;
2023-10-26 13:33:59 -07:00
this . loadSettings ( ) ;
this . trainerId = Utils . randSeedInt ( 65536 ) ;
this . secretId = Utils . randSeedInt ( 65536 ) ;
2023-04-28 22:40:24 -07:00
this . unlocks = {
2023-05-31 16:54:57 -07:00
[ Unlockables . ENDLESS_MODE ] : false ,
2023-11-04 16:46:48 -07:00
[ Unlockables . MINI_BLACK_HOLE ] : false ,
[ Unlockables . SPLICED_ENDLESS_MODE ] : false
2023-04-28 22:40:24 -07:00
} ;
2023-11-11 21:31:40 -08:00
this . achvUnlocks = { } ;
2023-12-19 20:51:48 -08:00
this . voucherUnlocks = { } ;
this . voucherCounts = {
[ VoucherType . REGULAR ] : 0 ,
[ VoucherType . PLUS ] : 0 ,
2023-12-20 14:22:50 -08:00
[ VoucherType . PREMIUM ] : 0 ,
[ VoucherType . GOLDEN ] : 0
2023-12-19 20:51:48 -08:00
} ;
2023-12-15 20:07:32 -08:00
this . eggs = [ ] ;
2023-04-26 13:07:29 -07:00
this . initDexData ( ) ;
2023-04-28 12:03:42 -07:00
this . loadSystem ( ) ;
2023-04-17 19:44:41 -07:00
}
2023-04-28 22:40:24 -07:00
public saveSystem ( ) : boolean {
2023-04-19 11:07:38 -07:00
if ( this . scene . quickStart )
return false ;
2023-04-28 12:03:42 -07:00
const data : SystemSaveData = {
2023-04-17 22:32:26 -07:00
trainerId : this.trainerId ,
secretId : this.secretId ,
2023-04-26 09:50:21 -07:00
dexData : this.dexData ,
2023-04-28 22:40:24 -07:00
unlocks : this.unlocks ,
2023-11-11 21:31:40 -08:00
achvUnlocks : this.achvUnlocks ,
2023-12-19 20:51:48 -08:00
voucherUnlocks : this.voucherUnlocks ,
voucherCounts : this.voucherCounts ,
2023-12-15 20:07:32 -08:00
eggs : this.eggs.map ( e = > new EggData ( e ) ) ,
2023-12-13 21:41:35 -08:00
gameVersion : this.scene.game.config.gameVersion ,
2023-04-26 09:50:21 -07:00
timestamp : new Date ( ) . getTime ( )
2023-04-17 22:32:26 -07:00
} ;
2023-11-12 20:47:04 -08:00
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 ) ) ) ;
2023-04-17 19:44:41 -07:00
2023-04-17 22:32:26 -07:00
return true ;
2023-04-17 19:44:41 -07:00
}
2023-04-28 12:03:42 -07:00
private loadSystem ( ) : boolean {
2023-10-26 13:33:59 -07:00
if ( ! localStorage . hasOwnProperty ( 'data' ) )
2023-04-17 22:32:26 -07:00
return false ;
2023-12-26 11:49:23 -08:00
const data = this . parseSystemData ( atob ( localStorage . getItem ( 'data' ) ) ) ;
2023-11-12 20:47:04 -08:00
2023-04-28 12:03:42 -07:00
console . debug ( data ) ;
2023-04-17 22:32:26 -07:00
2023-12-13 21:41:35 -08:00
/ * c o n s t v e r s i o n s = [ t h i s . s c e n e . g a m e . c o n f i g . g a m e V e r s i o n , d a t a . g a m e V e r s i o n | | ' 0 . 0 . 0 ' ] ;
if ( versions [ 0 ] !== versions [ 1 ] ) {
const [ versionNumbers , oldVersionNumbers ] = versions . map ( ver = > ver . split ( '.' ) . map ( v = > parseInt ( v ) ) ) ;
} * /
2023-04-17 22:32:26 -07:00
this . trainerId = data . trainerId ;
this . secretId = data . secretId ;
2023-04-17 19:44:41 -07:00
2023-04-28 22:40:24 -07:00
if ( data . unlocks ) {
for ( let key of Object . keys ( data . unlocks ) ) {
if ( this . unlocks . hasOwnProperty ( key ) )
this . unlocks [ key ] = data . unlocks [ key ] ;
}
}
2023-11-11 21:31:40 -08:00
if ( data . achvUnlocks ) {
for ( let a of Object . keys ( data . achvUnlocks ) ) {
if ( achvs . hasOwnProperty ( a ) )
this . achvUnlocks [ a ] = data . achvUnlocks [ a ] ;
2023-12-15 20:07:32 -08:00
}
2023-11-11 21:31:40 -08:00
}
2023-12-19 20:51:48 -08:00
if ( data . voucherUnlocks ) {
for ( let v of Object . keys ( data . voucherUnlocks ) ) {
if ( vouchers . hasOwnProperty ( v ) )
this . voucherUnlocks [ v ] = data . voucherUnlocks [ v ] ;
}
}
if ( data . voucherCounts ) {
Utils . getEnumKeys ( VoucherType ) . forEach ( key = > {
const index = VoucherType [ key ] ;
this . voucherCounts [ index ] = data . voucherCounts [ index ] || 0 ;
} ) ;
}
2023-12-15 20:07:32 -08:00
this . eggs = data . eggs
? data . eggs . map ( e = > e . toEgg ( ) )
: [ ] ;
2023-11-12 20:47:04 -08:00
if ( data . dexData [ 1 ] . hasOwnProperty ( 0 ) )
this . migrateLegacyDexData ( this . dexData , data . dexData ) ;
else
this . dexData = Object . assign ( this . dexData , data . dexData ) ;
2023-04-26 13:07:29 -07:00
2023-04-17 22:32:26 -07:00
return true ;
2023-04-17 19:44:41 -07:00
}
2023-12-26 11:49:23 -08:00
private parseSystemData ( dataStr : string ) : SystemSaveData {
return JSON . parse ( dataStr , ( k : string , v : any ) = > {
if ( k === 'eggs' ) {
const ret : EggData [ ] = [ ] ;
for ( let e of v )
ret . push ( new EggData ( e ) ) ;
return ret ;
}
return k . endsWith ( 'Attr' ) ? BigInt ( v ) : v ;
} ) as SystemSaveData ;
}
2023-10-26 13:33:59 -07:00
public saveSetting ( setting : Setting , valueIndex : integer ) : boolean {
let settings : object = { } ;
if ( localStorage . hasOwnProperty ( 'settings' ) )
settings = JSON . parse ( localStorage . getItem ( 'settings' ) ) ;
setSetting ( this . scene , setting as Setting , valueIndex ) ;
Object . keys ( settingDefaults ) . forEach ( s = > {
if ( s === setting )
settings [ s ] = valueIndex ;
} ) ;
localStorage . setItem ( 'settings' , JSON . stringify ( settings ) ) ;
return true ;
}
private loadSettings ( ) : boolean {
2023-12-25 12:03:50 -08:00
Object . values ( Setting ) . map ( setting = > setting as Setting ) . forEach ( setting = > setSetting ( this . scene , setting , settingDefaults [ setting ] ) ) ;
2023-10-26 13:33:59 -07:00
if ( ! localStorage . hasOwnProperty ( 'settings' ) )
return false ;
const settings = JSON . parse ( localStorage . getItem ( 'settings' ) ) ;
for ( let setting of Object . keys ( settings ) )
setSetting ( this . scene , setting as Setting , settings [ setting ] ) ;
}
2023-04-28 12:03:42 -07:00
saveSession ( scene : BattleScene ) : boolean {
const sessionData = {
2023-10-18 15:01:15 -07:00
seed : scene.seed ,
2023-05-31 16:54:57 -07:00
gameMode : scene.gameMode ,
2023-04-28 12:03:42 -07:00
party : scene.getParty ( ) . map ( p = > new PokemonData ( p ) ) ,
2023-10-07 13:08:33 -07:00
enemyParty : scene.getEnemyParty ( ) . map ( p = > new PokemonData ( p ) ) ,
2023-05-18 08:11:06 -07:00
modifiers : scene.findModifiers ( ( ) = > true ) . map ( m = > new PersistentModifierData ( m , true ) ) ,
enemyModifiers : scene.findModifiers ( ( ) = > true , false ) . map ( m = > new PersistentModifierData ( m , false ) ) ,
2023-04-28 12:03:42 -07:00
arena : new ArenaData ( scene . arena ) ,
pokeballCounts : scene.pokeballCounts ,
2023-11-10 12:51:34 -08:00
money : scene.money ,
2023-04-28 12:03:42 -07:00
waveIndex : scene.currentBattle.waveIndex ,
2023-10-07 13:08:33 -07:00
battleType : scene.currentBattle.battleType ,
trainer : scene.currentBattle.battleType == BattleType . TRAINER ? new TrainerData ( scene . currentBattle . trainer ) : null ,
2023-12-13 21:41:35 -08:00
gameVersion : scene.game.config.gameVersion ,
2023-04-28 12:03:42 -07:00
timestamp : new Date ( ) . getTime ( )
} as SessionSaveData ;
localStorage . setItem ( 'sessionData' , btoa ( JSON . stringify ( sessionData ) ) ) ;
console . debug ( 'Session data saved' ) ;
return true ;
}
hasSession() {
return ! ! localStorage . getItem ( 'sessionData' ) ;
}
loadSession ( scene : BattleScene ) : Promise < boolean > {
return new Promise ( async ( resolve , reject ) = > {
if ( ! this . hasSession ( ) )
return resolve ( false ) ;
try {
2023-12-26 11:49:23 -08:00
const sessionDataStr = atob ( localStorage . getItem ( 'sessionData' ) ) ;
const sessionData = this . parseSessionData ( sessionDataStr ) ;
2023-04-28 12:03:42 -07:00
console . debug ( sessionData ) ;
2023-12-13 21:41:35 -08:00
scene . seed = sessionData . seed || scene . game . config . seed [ 0 ] ;
2023-10-18 15:01:15 -07:00
scene . resetSeed ( ) ;
2023-05-31 16:54:57 -07:00
scene . gameMode = sessionData . gameMode || GameMode . CLASSIC ;
2023-04-28 12:03:42 -07:00
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
const party = scene . getParty ( ) ;
party . splice ( 0 , party . length ) ;
for ( let p of sessionData . party ) {
const pokemon = p . toPokemon ( scene ) as PlayerPokemon ;
pokemon . setVisible ( false ) ;
loadPokemonAssets . push ( pokemon . loadAssets ( ) ) ;
party . push ( pokemon ) ;
}
Object . keys ( scene . pokeballCounts ) . forEach ( ( key : string ) = > {
scene . pokeballCounts [ key ] = sessionData . pokeballCounts [ key ] || 0 ;
} ) ;
2023-11-10 12:51:34 -08:00
scene . money = sessionData . money || 0 ;
scene . updateMoneyText ( ) ;
2023-10-07 13:08:33 -07:00
const battleType = sessionData . battleType || 0 ;
const battle = scene . newBattle ( sessionData . waveIndex , battleType , sessionData . trainer , battleType === BattleType . TRAINER ? trainerConfigs [ sessionData . trainer . trainerType ] . isDouble : sessionData.enemyParty.length > 1 ) ;
2023-10-18 15:01:15 -07:00
scene . newArena ( sessionData . arena . biome , true ) ;
2023-10-07 13:08:33 -07:00
sessionData . enemyParty . forEach ( ( enemyData , e ) = > {
2023-10-28 07:51:34 -07:00
const enemyPokemon = enemyData . toPokemon ( scene , battleType ) as EnemyPokemon ;
2023-10-07 13:08:33 -07:00
battle . enemyParty [ e ] = enemyPokemon ;
2023-10-23 10:48:56 -07:00
if ( battleType === BattleType . WILD )
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2023-05-18 08:11:06 -07:00
loadPokemonAssets . push ( enemyPokemon . loadAssets ( ) ) ;
} ) ;
2023-04-28 12:03:42 -07:00
scene . arena . weather = sessionData . arena . weather ;
// TODO
//scene.arena.tags = sessionData.arena.tags;
const modifiersModule = await import ( '../modifier/modifier' ) ;
for ( let modifierData of sessionData . modifiers ) {
const modifier = modifierData . toModifier ( scene , modifiersModule [ modifierData . className ] ) ;
if ( modifier )
2023-10-31 18:43:22 -07:00
scene . addModifier ( modifier , true ) ;
2023-04-28 12:03:42 -07:00
}
2023-10-31 18:43:22 -07:00
scene . updateModifiers ( true ) ;
2023-04-28 12:03:42 -07:00
for ( let enemyModifierData of sessionData . enemyModifiers ) {
2023-10-28 10:24:57 -07:00
const modifier = enemyModifierData . toModifier ( scene , modifiersModule [ enemyModifierData . className ] ) ;
2023-04-28 12:03:42 -07:00
if ( modifier )
2023-10-31 18:43:22 -07:00
scene . addEnemyModifier ( modifier , true ) ;
2023-04-28 12:03:42 -07:00
}
2023-10-31 18:43:22 -07:00
scene . updateModifiers ( false ) ;
2023-04-28 12:03:42 -07:00
Promise . all ( loadPokemonAssets ) . then ( ( ) = > resolve ( true ) ) ;
} catch ( err ) {
reject ( err ) ;
return ;
}
} ) ;
}
clearSession ( ) : void {
localStorage . removeItem ( 'sessionData' ) ;
}
2023-12-26 11:49:23 -08:00
parseSessionData ( dataStr : string ) : SessionSaveData {
return JSON . parse ( dataStr , ( k : string , v : any ) = > {
/ * c o n s t v e r s i o n s = [ s c e n e . g a m e . c o n f i g . g a m e V e r s i o n , s e s s i o n D a t a . g a m e V e r s i o n | | ' 0 . 0 . 0 ' ] ;
if ( versions [ 0 ] !== versions [ 1 ] ) {
const [ versionNumbers , oldVersionNumbers ] = versions . map ( ver = > ver . split ( '.' ) . map ( v = > parseInt ( v ) ) ) ;
} * /
if ( k === 'party' || k === 'enemyParty' || k === 'enemyField' ) {
const ret : PokemonData [ ] = [ ] ;
for ( let pd of v )
ret . push ( new PokemonData ( pd ) ) ;
return ret ;
}
if ( k === 'trainer' )
return v ? new TrainerData ( v ) : null ;
if ( k === 'modifiers' || k === 'enemyModifiers' ) {
const player = k === 'modifiers' ;
const ret : PersistentModifierData [ ] = [ ] ;
for ( let md of v )
ret . push ( new PersistentModifierData ( md , player ) ) ;
return ret ;
}
if ( k === 'arena' )
return new ArenaData ( v ) ;
return v ;
} ) as SessionSaveData ;
}
public exportData ( dataType : GameDataType ) : void {
const dataKey : string = getDataTypeKey ( dataType ) ;
const dataStr = atob ( localStorage . getItem ( dataKey ) ) ;
const encryptedData = AES . encrypt ( dataStr , saveKey ) ;
const blob = new Blob ( [ encryptedData . toString ( ) ] , { type : 'text/json' } ) ;
const link = document . createElement ( 'a' ) ;
link . href = window . URL . createObjectURL ( blob ) ;
link . download = ` ${ dataKey } .prsv ` ;
link . click ( ) ;
link . remove ( ) ;
}
public importData ( dataType : GameDataType ) : void {
const dataKey = getDataTypeKey ( dataType ) ;
let saveFile : any = document . getElementById ( 'saveFile' ) ;
if ( saveFile )
saveFile . remove ( ) ;
saveFile = document . createElement ( 'input' ) ;
saveFile . id = 'saveFile' ;
saveFile . type = 'file' ;
saveFile . accept = '.prsv' ;
saveFile . style . display = 'none' ;
saveFile . addEventListener ( 'change' ,
e = > {
let reader = new FileReader ( ) ;
reader . onload = ( _ = > {
return e = > {
const dataStr = AES . decrypt ( e . target . result . toString ( ) , saveKey ) . toString ( enc . Utf8 ) ;
let valid = false ;
try {
switch ( dataType ) {
case GameDataType . SYSTEM :
const systemData = this . parseSystemData ( dataStr ) ;
valid = ! ! systemData . dexData && ! ! systemData . timestamp ;
break ;
case GameDataType . SESSION :
const sessionData = this . parseSessionData ( dataStr ) ;
valid = ! ! sessionData . party && ! ! sessionData . enemyParty && ! ! sessionData . timestamp ;
break ;
case GameDataType . SETTINGS :
valid = true ;
break ;
}
} catch ( ex ) {
console . error ( ex ) ;
}
let dataName : string ;
switch ( dataType ) {
case GameDataType . SYSTEM :
dataName = 'save' ;
break ;
case GameDataType . SESSION :
dataName = 'session' ;
break ;
case GameDataType . SETTINGS :
dataName = 'settings' ;
break ;
}
if ( ! valid )
return this . scene . ui . showText ( ` Your ${ dataName } data could not be loaded. It may be corrupted. ` , null , ( ) = > this . scene . ui . showText ( null , 0 ) , Utils . fixedInt ( 1500 ) ) ;
this . scene . ui . showText ( ` Your ${ dataName } data will be overridden and the page will reload. Proceed? ` , null , ( ) = > {
this . scene . ui . setOverlayMode ( Mode . CONFIRM , ( ) = > {
localStorage . setItem ( dataKey , btoa ( dataStr ) ) ;
window . location = window . location ;
} , ( ) = > {
this . scene . ui . revertMode ( ) ;
this . scene . ui . showText ( null , 0 ) ;
} , false , 98 ) ;
} ) ;
} ;
} ) ( ( e . target as any ) . files [ 0 ] ) ;
reader . readAsText ( ( e . target as any ) . files [ 0 ] ) ;
}
) ;
saveFile . click ( ) ;
/ * ( t h i s . s c e n e . p l u g i n s . g e t ( ' r e x f i l e c h o o s e r p l u g i n ' ) a s F i l e C h o o s e r P l u g i n ) . o p e n ( { a c c e p t : ' . p r s v ' } )
. then ( result = > {
} ) ; * /
}
2023-04-28 12:03:42 -07:00
private initDexData ( ) : void {
2023-04-17 19:44:41 -07:00
const data : DexData = { } ;
for ( let species of allSpecies ) {
2023-11-12 20:47:04 -08:00
data [ species . speciesId ] = {
seenAttr : 0n , caughtAttr : 0n , seenCount : 0 , caughtCount : 0 , ivs : [ 0 , 0 , 0 , 0 , 0 , 0 ]
} ;
2023-04-17 19:44:41 -07:00
}
const defaultStarters : Species [ ] = [
Species . BULBASAUR , Species . CHARMANDER , Species . SQUIRTLE ,
Species . CHIKORITA , Species . CYNDAQUIL , Species . TOTODILE ,
Species . TREECKO , Species . TORCHIC , Species . MUDKIP ,
Species . TURTWIG , Species . CHIMCHAR , Species . PIPLUP ,
2023-12-08 13:29:03 -08:00
Species . SNIVY , Species . TEPIG , Species . OSHAWOTT ,
Species . CHESPIN , Species . FENNEKIN , Species . FROAKIE ,
Species . ROWLET , Species . LITTEN , Species . POPPLIO ,
Species . GROOKEY , Species . SCORBUNNY , Species . SOBBLE ,
Species . SPRIGATITO , Species . FUECOCO , Species . QUAXLY
2023-04-17 19:44:41 -07:00
] ;
2023-11-12 20:47:04 -08:00
const defaultStarterAttr = DexAttr . NON_SHINY | DexAttr . MALE | DexAttr . ABILITY_1 | DexAttr . DEFAULT_FORM ;
2023-04-17 19:44:41 -07:00
for ( let ds of defaultStarters ) {
2023-11-12 20:47:04 -08:00
let entry = data [ ds ] as DexEntry ;
entry . seenAttr = defaultStarterAttr ;
entry . caughtAttr = defaultStarterAttr ;
for ( let i in entry . ivs )
entry . ivs [ i ] = 10 ;
2023-04-17 22:32:26 -07:00
}
2023-04-17 19:44:41 -07:00
2023-04-17 22:32:26 -07:00
this . dexData = data ;
}
2023-11-12 20:47:04 -08:00
setPokemonSeen ( pokemon : Pokemon , incrementCount : boolean = true ) : void {
const dexEntry = this . dexData [ pokemon . species . speciesId ] ;
2023-11-13 05:20:31 -08:00
dexEntry . seenAttr |= pokemon . getDexAttr ( ) ;
2023-11-12 20:47:04 -08:00
if ( incrementCount )
dexEntry . seenCount ++ ;
2023-04-17 22:32:26 -07:00
}
2023-04-17 19:44:41 -07:00
2023-11-12 20:47:04 -08:00
setPokemonCaught ( pokemon : Pokemon , incrementCount : boolean = true ) : Promise < void > {
return this . setPokemonSpeciesCaught ( pokemon , pokemon . species , incrementCount ) ;
2023-07-05 11:19:49 -07:00
}
2023-11-12 20:47:04 -08:00
setPokemonSpeciesCaught ( pokemon : Pokemon , species : PokemonSpecies , incrementCount? : boolean ) : Promise < void > {
2023-07-05 11:19:49 -07:00
return new Promise < void > ( ( resolve ) = > {
2023-11-12 20:47:04 -08:00
const dexEntry = this . dexData [ species . speciesId ] ;
const caughtAttr = dexEntry . caughtAttr ;
2023-11-13 05:20:31 -08:00
dexEntry . caughtAttr |= pokemon . getDexAttr ( ) ;
2023-11-12 20:47:04 -08:00
if ( incrementCount )
dexEntry . seenCount ++ ;
2023-04-17 22:32:26 -07:00
2023-11-12 20:47:04 -08:00
const hasPrevolution = pokemonPrevolutions . hasOwnProperty ( species . speciesId ) ;
const newCatch = ! caughtAttr ;
2023-04-17 22:32:26 -07:00
2023-12-01 14:23:26 -08:00
const checkPrevolution = ( ) = > {
if ( hasPrevolution ) {
const prevolutionSpecies = pokemonPrevolutions [ species . speciesId ] ;
return this . setPokemonSpeciesCaught ( pokemon , getPokemonSpecies ( prevolutionSpecies ) ) . then ( ( ) = > resolve ( ) ) ;
} else
resolve ( ) ;
} ;
2023-04-17 22:32:26 -07:00
2023-12-01 14:23:26 -08:00
if ( newCatch && speciesStarters . hasOwnProperty ( species . speciesId ) ) {
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' , 1500 ) ;
this . scene . ui . showText ( ` ${ species . name } has been \ nadded as a starter! ` , null , ( ) = > checkPrevolution ( ) , null , true ) ;
2023-07-05 11:19:49 -07:00
} else
2023-12-01 14:23:26 -08:00
checkPrevolution ( ) ;
2023-04-17 22:32:26 -07:00
} ) ;
}
2023-12-19 20:51:48 -08:00
updateSpeciesDexIvs ( speciesId : Species , ivs : integer [ ] ) : void {
let dexEntry : DexEntry ;
do {
dexEntry = this . scene . gameData . dexData [ speciesId ] ;
const dexIvs = dexEntry . ivs ;
for ( let i = 0 ; i < dexIvs . length ; i ++ ) {
if ( dexIvs [ i ] < ivs [ i ] )
dexIvs [ i ] = ivs [ i ] ;
}
if ( dexIvs . filter ( iv = > iv === 31 ) . length === 6 )
this . scene . validateAchv ( achvs . PERFECT_IVS ) ;
} while ( pokemonPrevolutions . hasOwnProperty ( speciesId ) && ( speciesId = pokemonPrevolutions [ speciesId ] ) ) ;
}
2023-11-12 20:47:04 -08:00
getSpeciesDefaultDexAttr ( species : PokemonSpecies ) : bigint {
let ret = 0 n ;
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 ;
2023-04-17 19:44:41 -07:00
}
2023-11-12 20:47:04 -08:00
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 ) ;
return {
shiny ,
female ,
abilityIndex ,
formIndex
2023-04-17 19:44:41 -07:00
} ;
2023-11-12 20:47:04 -08:00
}
2023-04-17 19:44:41 -07:00
2023-11-12 20:47:04 -08:00
getFormIndex ( attr : bigint ) : integer {
if ( ! attr || attr < DexAttr . DEFAULT_FORM )
return 0 ;
let f = 0 ;
while ( ! ( attr & this . getFormAttr ( f ) ) )
f ++ ;
return f ;
}
2023-04-17 19:44:41 -07:00
2023-11-12 20:47:04 -08:00
getFormAttr ( formIndex : integer ) : bigint {
return BigInt ( Math . pow ( 2 , 7 + formIndex ) ) ;
2023-04-17 19:44:41 -07:00
}
2023-11-12 20:47:04 -08:00
// 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 = 0 n ;
let caughtAttr = 0 n ;
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 ;
}
} ) ;
2023-04-26 09:50:21 -07:00
} ) ;
2023-11-12 20:47:04 -08:00
} 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 ;
}
2023-04-17 19:44:41 -07:00
} ) ;
}
2023-11-12 20:47:04 -08:00
}
2023-04-26 09:50:21 -07:00
} ) ;
2023-11-12 20:47:04 -08:00
newEntry . seenAttr = seenAttr ;
newEntry . caughtAttr = caughtAttr ;
2023-04-26 09:50:21 -07:00
}
2023-11-12 20:47:04 -08:00
return newDexData ;
2023-04-26 09:50:21 -07:00
}
2023-04-17 19:44:41 -07:00
}