2023-12-31 18:30:37 -05:00
import BattleScene , { PokeballCounts , bypassLogin } from "../battle-scene" ;
2024-02-29 20:08:50 -05:00
import Pokemon , { EnemyPokemon , PlayerPokemon } from "../field/pokemon" ;
2024-04-20 22:30:28 -04:00
import { pokemonEvolutions , pokemonPrevolutions } from "../data/pokemon-evolutions" ;
2024-04-21 16:19:11 -04:00
import PokemonSpecies , { allSpecies , getPokemonSpecies , noStarterFormKeys , speciesStarters } from "../data/pokemon-species" ;
2024-04-19 23:37:23 -04:00
import { Species , defaultStarterSpecies } from "../data/enums/species" ;
2023-04-20 15:46:05 -04:00
import * as Utils from "../utils" ;
2023-04-28 15:03:42 -04:00
import PokemonData from "./pokemon-data" ;
import PersistentModifierData from "./modifier-data" ;
import ArenaData from "./arena-data" ;
2023-04-29 01:40:24 -04:00
import { Unlockables } from "./unlockables" ;
2024-03-14 16:26:57 -04:00
import { GameModes , gameModes } from "../game-mode" ;
2023-10-07 16:08:33 -04:00
import { BattleType } from "../battle" ;
import TrainerData from "./trainer-data" ;
2024-01-13 12:24:24 -05:00
import { trainerConfigs } from "../data/trainer-config" ;
2023-10-26 16:33:59 -04:00
import { Setting , setSetting , settingDefaults } from "./settings" ;
2023-11-12 00:31:40 -05:00
import { achvs } from "./achv" ;
2023-12-15 23:07:32 -05:00
import EggData from "./egg-data" ;
import { Egg } from "../data/egg" ;
2023-12-19 23:51:48 -05:00
import { VoucherType , vouchers } from "./voucher" ;
2023-12-26 14:49:23 -05:00
import { AES , enc } from "crypto-js" ;
import { Mode } from "../ui/ui" ;
2023-12-31 22:49:50 -05:00
import { loggedInUser , updateUserInfo } from "../account" ;
2024-01-05 22:24:05 -05:00
import { Nature } from "../data/nature" ;
2024-01-11 12:26:32 -05:00
import { GameStats } from "./game-stats" ;
2024-02-13 18:42:11 -05:00
import { Tutorial } from "../tutorial" ;
2024-02-21 01:03:34 -05:00
import { Moves } from "../data/enums/moves" ;
2024-02-25 12:45:41 -05:00
import { speciesEggMoves } from "../data/egg-moves" ;
import { allMoves } from "../data/move" ;
2024-03-21 00:57:28 -04:00
import { TrainerVariant } from "../field/trainer" ;
2024-04-21 16:19:11 -04:00
import { OutdatedPhase , ReloadSessionPhase } from "#app/phases" ;
2024-04-20 22:30:28 -04:00
import { Variant , variantData } from "#app/data/variant" ;
2023-12-26 14:49:23 -05:00
const saveKey = 'x0i2O7WRiANTqPmZ' ; // Temporary; secure encryption is not yet necessary
export enum GameDataType {
SYSTEM ,
SESSION ,
2024-02-13 18:42:11 -05:00
SETTINGS ,
TUTORIALS
2023-12-26 14:49:23 -05:00
}
2024-02-06 16:15:35 -05:00
export enum PlayerGender {
UNSET ,
MALE ,
FEMALE
}
2024-04-13 18:59:58 -04:00
export enum Passive {
UNLOCKED = 1 ,
ENABLED = 2
}
2024-03-15 15:13:32 -04:00
export function getDataTypeKey ( dataType : GameDataType , slotId : integer = 0 ) : string {
2023-12-26 14:49:23 -05:00
switch ( dataType ) {
case GameDataType . SYSTEM :
return 'data' ;
case GameDataType . SESSION :
2024-03-15 15:13:32 -04:00
let ret = 'sessionData' ;
if ( slotId )
ret += slotId ;
return ret ;
2023-12-26 14:49:23 -05:00
case GameDataType . SETTINGS :
return 'settings' ;
2024-02-13 18:42:11 -05:00
case GameDataType . TUTORIALS :
return 'tutorials' ;
2023-12-26 14:49:23 -05:00
}
}
2023-04-17 22:44:41 -04:00
2023-04-28 15:03:42 -04:00
interface SystemSaveData {
2023-04-18 01:32:26 -04:00
trainerId : integer ;
secretId : integer ;
2024-02-06 16:15:35 -05:00
gender : PlayerGender ;
2023-04-18 01:32:26 -04:00
dexData : DexData ;
2024-04-13 18:59:58 -04:00
starterData : StarterData ;
2024-01-11 12:26:32 -05:00
gameStats : GameStats ;
2023-04-29 01:40:24 -04:00
unlocks : Unlocks ;
2023-11-12 00:31:40 -05:00
achvUnlocks : AchvUnlocks ;
2023-12-19 23:51:48 -05:00
voucherUnlocks : VoucherUnlocks ;
voucherCounts : VoucherCounts ;
2023-12-15 23:07:32 -05:00
eggs : EggData [ ] ;
2023-12-14 00:41:35 -05:00
gameVersion : string ;
2023-04-28 15:03:42 -04:00
timestamp : integer ;
}
2024-03-15 15:13:32 -04:00
export interface SessionSaveData {
2023-10-18 18:01:15 -04:00
seed : string ;
2024-01-11 20:27:50 -05:00
playTime : integer ;
2024-03-14 16:26:57 -04:00
gameMode : GameModes ;
2023-04-28 15:03:42 -04:00
party : PokemonData [ ] ;
2023-10-07 16:08:33 -04:00
enemyParty : PokemonData [ ] ;
2023-04-28 15:03:42 -04:00
modifiers : PersistentModifierData [ ] ;
enemyModifiers : PersistentModifierData [ ] ;
arena : ArenaData ;
pokeballCounts : PokeballCounts ;
2023-11-10 15:51:34 -05:00
money : integer ;
2024-03-17 11:36:19 -04:00
score : integer ;
2023-04-28 15:03:42 -04:00
waveIndex : integer ;
2023-10-07 16:08:33 -04:00
battleType : BattleType ;
trainer : TrainerData ;
2023-12-14 00:41:35 -05:00
gameVersion : string ;
2023-04-28 15:03:42 -04:00
timestamp : integer ;
2023-04-18 01:32:26 -04:00
}
2023-04-29 01:40:24 -04:00
interface Unlocks {
[ key : integer ] : boolean ;
}
2023-11-12 00:31:40 -05:00
interface AchvUnlocks {
[ key : string ] : integer
}
2023-12-19 23:51:48 -05:00
interface VoucherUnlocks {
[ key : string ] : integer
}
export interface VoucherCounts {
[ type : string ] : integer ;
}
2023-04-17 22:44:41 -04:00
export interface DexData {
2023-11-12 23:47:04 -05:00
[ key : integer ] : DexEntry
2023-04-17 22:44:41 -04:00
}
export interface DexEntry {
2023-11-12 23:47:04 -05:00
seenAttr : bigint ;
caughtAttr : bigint ;
2024-01-05 22:24:05 -05:00
natureAttr : integer ,
2023-11-12 23:47:04 -05:00
seenCount : integer ;
caughtCount : integer ;
2023-12-31 13:20:28 -05:00
hatchedCount : integer ;
2023-11-12 23:47:04 -05:00
ivs : integer [ ] ;
2023-04-17 22:44:41 -04:00
}
2023-11-12 23:47:04 -05:00
export const DexAttr = {
NON_SHINY : 1n ,
SHINY : 2n ,
MALE : 4n ,
FEMALE : 8n ,
2024-04-18 22:52:26 -04:00
DEFAULT_VARIANT : 16n ,
VARIANT_2 : 32n ,
VARIANT_3 : 64n ,
2023-11-12 23:47:04 -05:00
DEFAULT_FORM : 128n
}
export interface DexAttrProps {
2023-04-17 22:44:41 -04:00
shiny : boolean ;
female : boolean ;
2024-04-18 22:52:26 -04:00
variant : Variant ;
2023-11-12 23:47:04 -05:00
formIndex : integer ;
2023-04-17 22:44:41 -04:00
}
2024-04-18 22:52:26 -04:00
export const AbilityAttr = {
ABILITY_1 : 1 ,
ABILITY_2 : 2 ,
ABILITY_HIDDEN : 4
}
2024-02-21 01:03:34 -05:00
export type StarterMoveset = [ Moves ] | [ Moves , Moves ] | [ Moves , Moves , Moves ] | [ Moves , Moves , Moves , Moves ] ;
2024-04-13 18:59:58 -04:00
export interface StarterFormMoveData {
[ key : integer ] : StarterMoveset
}
2024-02-21 01:03:34 -05:00
export interface StarterMoveData {
[ key : integer ] : StarterMoveset | StarterFormMoveData
}
2024-04-13 18:59:58 -04:00
export interface StarterDataEntry {
moveset : StarterMoveset | StarterFormMoveData ;
eggMoves : integer ;
candyCount : integer ;
2024-04-18 22:52:26 -04:00
abilityAttr : integer ;
2024-04-13 18:59:58 -04:00
passiveAttr : integer ;
valueReduction : integer ;
2024-02-21 01:03:34 -05:00
}
2024-04-13 18:59:58 -04:00
export interface StarterData {
[ key : integer ] : StarterDataEntry
2024-02-25 12:45:41 -05:00
}
2024-02-13 18:42:11 -05:00
export interface TutorialFlags {
[ key : string ] : boolean
}
2023-12-31 13:39:04 -05:00
const systemShortKeys = {
seenAttr : '$sa' ,
caughtAttr : '$ca' ,
2024-01-05 22:24:05 -05:00
natureAttr : '$na' ,
2023-12-31 13:39:04 -05:00
seenCount : '$s' ,
caughtCount : '$c' ,
2024-04-13 18:59:58 -04:00
ivs : '$i' ,
moveset : '$m' ,
eggMoves : '$em' ,
2024-04-15 19:47:12 -04:00
candyCount : '$x' ,
2024-04-13 18:59:58 -04:00
passive : '$p' ,
valueReduction : '$vr'
2023-12-31 13:39:04 -05:00
} ;
2023-04-18 01:32:26 -04:00
export class GameData {
2023-04-17 22:44:41 -04:00
private scene : BattleScene ;
public trainerId : integer ;
public secretId : integer ;
2024-02-06 16:15:35 -05:00
public gender : PlayerGender ;
2023-04-17 22:44:41 -04:00
public dexData : DexData ;
2024-01-05 22:24:05 -05:00
private defaultDexData : DexData ;
2023-04-17 22:44:41 -04:00
2024-04-13 18:59:58 -04:00
public starterData : StarterData ;
2024-02-25 12:45:41 -05:00
2024-01-11 12:26:32 -05:00
public gameStats : GameStats ;
2023-04-29 01:40:24 -04:00
public unlocks : Unlocks ;
2023-11-12 00:31:40 -05:00
public achvUnlocks : AchvUnlocks ;
2023-12-19 23:51:48 -05:00
public voucherUnlocks : VoucherUnlocks ;
public voucherCounts : VoucherCounts ;
2023-12-15 23:07:32 -05:00
public eggs : Egg [ ] ;
2023-04-17 22:44:41 -04:00
constructor ( scene : BattleScene ) {
this . scene = scene ;
2023-10-26 16:33:59 -04:00
this . loadSettings ( ) ;
this . trainerId = Utils . randSeedInt ( 65536 ) ;
this . secretId = Utils . randSeedInt ( 65536 ) ;
2024-04-13 18:59:58 -04:00
this . starterData = { } ;
2024-01-11 12:26:32 -05:00
this . gameStats = new GameStats ( ) ;
2023-04-29 01:40:24 -04:00
this . unlocks = {
2023-05-31 19:54:57 -04:00
[ Unlockables . ENDLESS_MODE ] : false ,
2023-11-04 19:46:48 -04:00
[ Unlockables . MINI_BLACK_HOLE ] : false ,
[ Unlockables . SPLICED_ENDLESS_MODE ] : false
2023-04-29 01:40:24 -04:00
} ;
2023-11-12 00:31:40 -05:00
this . achvUnlocks = { } ;
2023-12-19 23:51:48 -05:00
this . voucherUnlocks = { } ;
this . voucherCounts = {
[ VoucherType . REGULAR ] : 0 ,
[ VoucherType . PLUS ] : 0 ,
2023-12-20 17:22:50 -05:00
[ VoucherType . PREMIUM ] : 0 ,
[ VoucherType . GOLDEN ] : 0
2023-12-19 23:51:48 -05:00
} ;
2023-12-15 23:07:32 -05:00
this . eggs = [ ] ;
2023-04-26 16:07:29 -04:00
this . initDexData ( ) ;
2024-04-13 18:59:58 -04:00
this . initStarterData ( ) ;
2023-04-17 22:44:41 -04:00
}
2023-12-30 18:41:25 -05:00
public saveSystem ( ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
2024-04-04 10:16:29 -04:00
this . scene . ui . savingIcon . show ( ) ;
2024-04-10 01:32:49 -04:00
updateUserInfo ( ) . then ( response = > {
if ( ! response [ 0 ] ) {
2024-04-04 10:16:29 -04:00
this . scene . ui . savingIcon . hide ( ) ;
2023-12-30 18:41:25 -05:00
return resolve ( false ) ;
2024-04-04 10:16:29 -04:00
}
2023-12-30 18:41:25 -05:00
const data : SystemSaveData = {
trainerId : this.trainerId ,
secretId : this.secretId ,
2024-02-06 16:15:35 -05:00
gender : this.gender ,
2023-12-30 18:41:25 -05:00
dexData : this.dexData ,
2024-04-13 18:59:58 -04:00
starterData : this.starterData ,
2024-01-11 12:26:32 -05:00
gameStats : this.gameStats ,
2023-12-30 18:41:25 -05:00
unlocks : this.unlocks ,
achvUnlocks : this.achvUnlocks ,
voucherUnlocks : this.voucherUnlocks ,
voucherCounts : this.voucherCounts ,
eggs : this.eggs.map ( e = > new EggData ( e ) ) ,
gameVersion : this.scene.game.config.gameVersion ,
timestamp : new Date ( ) . getTime ( )
} ;
2023-12-31 18:30:37 -05:00
2023-12-30 18:41:25 -05:00
const maxIntAttrValue = Math . pow ( 2 , 31 ) ;
2023-12-31 18:30:37 -05:00
const systemData = JSON . stringify ( data , ( k : any , v : any ) = > typeof v === 'bigint' ? v <= maxIntAttrValue ? Number ( v ) : v . toString ( ) : v ) ;
if ( ! bypassLogin ) {
2024-04-24 19:26:04 -04:00
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SYSTEM } ` , systemData , undefined , true )
2023-12-31 18:30:37 -05:00
. then ( response = > response . text ( ) )
. then ( error = > {
2024-04-04 10:16:29 -04:00
this . scene . ui . savingIcon . hide ( ) ;
2023-12-31 18:30:37 -05:00
if ( error ) {
2024-04-15 10:09:51 -04:00
if ( error . startsWith ( 'client version out of date' ) ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new OutdatedPhase ( this . scene ) ) ;
2024-04-21 10:59:50 -04:00
} else if ( error . startsWith ( 'session out of date' ) ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
2024-04-15 10:09:51 -04:00
}
2023-12-31 18:30:37 -05:00
console . error ( error ) ;
return resolve ( false ) ;
}
resolve ( true ) ;
} ) ;
} else {
localStorage . setItem ( 'data_bak' , localStorage . getItem ( 'data' ) ) ;
localStorage . setItem ( 'data' , btoa ( systemData ) ) ;
2024-01-10 14:02:43 -05:00
2024-04-04 10:16:29 -04:00
this . scene . ui . savingIcon . hide ( ) ;
2024-01-10 14:02:43 -05:00
resolve ( true ) ;
2023-12-31 18:30:37 -05:00
}
2023-12-30 18:41:25 -05:00
} ) ;
} ) ;
2023-04-17 22:44:41 -04:00
}
2024-02-04 00:49:57 -05:00
public loadSystem ( ) : Promise < boolean > {
2023-12-31 18:30:37 -05:00
return new Promise < boolean > ( resolve = > {
if ( bypassLogin && ! localStorage . hasOwnProperty ( 'data' ) )
2024-02-19 20:36:10 -05:00
return resolve ( false ) ;
2023-04-18 01:32:26 -04:00
2023-12-31 18:30:37 -05:00
const handleSystemData = ( systemDataStr : string ) = > {
2024-02-19 20:36:10 -05:00
try {
const systemData = this . parseSystemData ( systemDataStr ) ;
2023-11-12 23:47:04 -05:00
2024-02-19 20:36:10 -05:00
console . debug ( systemData ) ;
2023-04-18 01:32:26 -04:00
2024-02-19 20:36:10 -05: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-12-14 00:41:35 -05:00
2024-02-19 20:36:10 -05:00
this . trainerId = systemData . trainerId ;
this . secretId = systemData . secretId ;
2023-04-17 22:44:41 -04:00
2024-02-19 20:36:10 -05:00
this . gender = systemData . gender ;
2024-02-06 16:15:35 -05:00
2024-02-19 20:36:10 -05:00
this . saveSetting ( Setting . Player_Gender , systemData . gender === PlayerGender . FEMALE ? 1 : 0 ) ;
2024-02-06 16:15:35 -05:00
2024-04-13 18:59:58 -04:00
const initStarterData = ! systemData . starterData ;
if ( initStarterData ) {
this . initStarterData ( ) ;
2024-03-01 12:41:47 -05:00
2024-04-13 18:59:58 -04:00
if ( systemData [ 'starterMoveData' ] ) {
const starterMoveData = systemData [ 'starterMoveData' ] ;
for ( let s of Object . keys ( starterMoveData ) )
this . starterData [ s ] . moveset = starterMoveData [ s ] ;
2024-03-01 12:41:47 -05:00
}
2024-04-13 18:59:58 -04:00
if ( systemData [ 'starterEggMoveData' ] ) {
const starterEggMoveData = systemData [ 'starterEggMoveData' ] ;
for ( let s of Object . keys ( starterEggMoveData ) )
this . starterData [ s ] . eggMoves = starterEggMoveData [ s ] ;
}
2024-04-20 22:30:28 -04:00
2024-04-21 10:59:50 -04:00
this . migrateStarterAbilities ( systemData , this . starterData ) ;
2024-04-18 22:52:26 -04:00
} else {
if ( [ '1.0.0' , '1.0.1' ] . includes ( systemData . gameVersion ) )
this . migrateStarterAbilities ( systemData ) ;
2024-04-24 09:26:46 -04:00
//this.fixVariantData(systemData);
2024-04-23 22:16:49 -04:00
this . fixStarterData ( systemData ) ;
2024-04-19 12:24:52 -04:00
// Migrate ability starter data if empty for caught species
Object . keys ( systemData . starterData ) . forEach ( sd = > {
if ( systemData . dexData [ sd ] . caughtAttr && ! systemData . starterData [ sd ] . abilityAttr )
systemData . starterData [ sd ] . abilityAttr = 1 ;
} ) ;
2024-04-13 18:59:58 -04:00
this . starterData = systemData . starterData ;
2024-04-18 22:52:26 -04:00
}
2024-02-21 01:03:34 -05:00
2024-02-19 20:36:10 -05:00
if ( systemData . gameStats )
this . gameStats = systemData . gameStats ;
2024-01-11 12:26:32 -05:00
2024-02-19 20:36:10 -05:00
if ( systemData . unlocks ) {
for ( let key of Object . keys ( systemData . unlocks ) ) {
if ( this . unlocks . hasOwnProperty ( key ) )
this . unlocks [ key ] = systemData . unlocks [ key ] ;
}
2023-12-31 18:30:37 -05:00
}
2023-12-19 23:51:48 -05:00
2024-02-19 20:36:10 -05:00
if ( systemData . achvUnlocks ) {
for ( let a of Object . keys ( systemData . achvUnlocks ) ) {
if ( achvs . hasOwnProperty ( a ) )
this . achvUnlocks [ a ] = systemData . achvUnlocks [ a ] ;
}
}
2023-12-31 18:30:37 -05:00
2024-02-19 20:36:10 -05:00
if ( systemData . voucherUnlocks ) {
for ( let v of Object . keys ( systemData . voucherUnlocks ) ) {
if ( vouchers . hasOwnProperty ( v ) )
this . voucherUnlocks [ v ] = systemData . voucherUnlocks [ v ] ;
}
2023-12-31 18:30:37 -05:00
}
2023-12-19 23:51:48 -05:00
2024-02-19 20:36:10 -05:00
if ( systemData . voucherCounts ) {
Utils . getEnumKeys ( VoucherType ) . forEach ( key = > {
const index = VoucherType [ key ] ;
this . voucherCounts [ index ] = systemData . voucherCounts [ index ] || 0 ;
} ) ;
}
2023-12-15 23:07:32 -05:00
2024-02-19 20:36:10 -05:00
this . eggs = systemData . eggs
? systemData . eggs . map ( e = > e . toEgg ( ) )
: [ ] ;
2023-04-26 16:07:29 -04:00
2024-02-19 20:36:10 -05:00
this . dexData = Object . assign ( this . dexData , systemData . dexData ) ;
this . consolidateDexData ( this . dexData ) ;
this . defaultDexData = null ;
2023-12-31 18:30:37 -05:00
2024-04-13 18:59:58 -04:00
if ( initStarterData ) {
const starterIds = Object . keys ( this . starterData ) . map ( s = > parseInt ( s ) as Species ) ;
for ( let s of starterIds ) {
this . starterData [ s ] . candyCount += this . dexData [ s ] . caughtCount ;
this . starterData [ s ] . candyCount += this . dexData [ s ] . hatchedCount * 2 ;
if ( this . dexData [ s ] . caughtAttr & DexAttr . SHINY )
this . starterData [ s ] . candyCount += 4 ;
}
}
2024-02-19 20:36:10 -05:00
resolve ( true ) ;
} catch ( err ) {
console . error ( err ) ;
resolve ( false ) ;
}
2023-12-31 18:30:37 -05:00
}
if ( ! bypassLogin ) {
2024-04-19 17:35:49 -04:00
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SYSTEM } ` , true )
2023-12-31 18:30:37 -05:00
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
2024-04-11 00:18:16 -04:00
if ( response . startsWith ( 'failed to open save file' ) ) {
2024-04-01 15:18:15 -04:00
this . scene . queueMessage ( 'Save data could not be found. If this is a new account, you can safely ignore this message.' , null , true ) ;
return resolve ( true ) ;
}
2023-12-31 18:30:37 -05:00
console . error ( response ) ;
return resolve ( false ) ;
}
handleSystemData ( response ) ;
} ) ;
} else
handleSystemData ( atob ( localStorage . getItem ( 'data' ) ) ) ;
} ) ;
2023-04-17 22:44:41 -04:00
}
2023-12-26 14:49:23 -05:00
private parseSystemData ( dataStr : string ) : SystemSaveData {
return JSON . parse ( dataStr , ( k : string , v : any ) = > {
2024-01-11 12:26:32 -05:00
if ( k === 'gameStats' )
return new GameStats ( v ) ;
else if ( k === 'eggs' ) {
2023-12-26 14:49:23 -05:00
const ret : EggData [ ] = [ ] ;
2023-12-31 18:53:16 -05:00
if ( v === null )
v = [ ] ;
2023-12-26 14:49:23 -05:00
for ( let e of v )
ret . push ( new EggData ( e ) ) ;
return ret ;
}
2024-04-18 22:52:26 -04:00
return k . endsWith ( 'Attr' ) && ! [ 'natureAttr' , 'abilityAttr' , 'passiveAttr' ] . includes ( k ) ? BigInt ( v ) : v ;
2023-12-26 14:49:23 -05:00
} ) as SystemSaveData ;
}
2023-12-31 13:39:04 -05:00
private convertSystemDataStr ( dataStr : string , shorten : boolean = false ) : string {
const fromKeys = shorten ? Object . keys ( systemShortKeys ) : Object . values ( systemShortKeys ) ;
const toKeys = shorten ? Object . values ( systemShortKeys ) : Object . keys ( systemShortKeys ) ;
for ( let k in fromKeys )
dataStr = dataStr . replace ( new RegExp ( ` ${ fromKeys [ k ] . replace ( '$' , '\\$' ) } ` , 'g' ) , toKeys [ k ] ) ;
return dataStr ;
}
2023-10-26 16:33:59 -04: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 15:03:50 -05:00
Object . values ( Setting ) . map ( setting = > setting as Setting ) . forEach ( setting = > setSetting ( this . scene , setting , settingDefaults [ setting ] ) ) ;
2023-10-26 16:33:59 -04: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 ] ) ;
}
2024-02-13 18:42:11 -05:00
public saveTutorialFlag ( tutorial : Tutorial , flag : boolean ) : boolean {
let tutorials : object = { } ;
if ( localStorage . hasOwnProperty ( 'tutorials' ) )
tutorials = JSON . parse ( localStorage . getItem ( 'tutorials' ) ) ;
Object . keys ( Tutorial ) . map ( t = > t as Tutorial ) . forEach ( t = > {
const key = Tutorial [ t ] ;
if ( key === tutorial )
tutorials [ key ] = flag ;
else
tutorials [ key ] ? ? = false ;
} ) ;
localStorage . setItem ( 'tutorials' , JSON . stringify ( tutorials ) ) ;
return true ;
}
public getTutorialFlags ( ) : TutorialFlags {
const ret : TutorialFlags = { } ;
Object . values ( Tutorial ) . map ( tutorial = > tutorial as Tutorial ) . forEach ( tutorial = > ret [ Tutorial [ tutorial ] ] = false ) ;
if ( ! localStorage . hasOwnProperty ( 'tutorials' ) )
return ret ;
const tutorials = JSON . parse ( localStorage . getItem ( 'tutorials' ) ) ;
for ( let tutorial of Object . keys ( tutorials ) )
ret [ tutorial ] = tutorials [ tutorial ] ;
return ret ;
}
2024-03-17 11:36:19 -04:00
private getSessionSaveData ( scene : BattleScene ) : SessionSaveData {
return {
seed : scene.seed ,
playTime : scene.sessionPlayTime ,
gameMode : scene.gameMode.modeId ,
party : scene.getParty ( ) . map ( p = > new PokemonData ( p ) ) ,
enemyParty : scene.getEnemyParty ( ) . map ( p = > new PokemonData ( p ) ) ,
modifiers : scene.findModifiers ( ( ) = > true ) . map ( m = > new PersistentModifierData ( m , true ) ) ,
enemyModifiers : scene.findModifiers ( ( ) = > true , false ) . map ( m = > new PersistentModifierData ( m , false ) ) ,
arena : new ArenaData ( scene . arena ) ,
pokeballCounts : scene.pokeballCounts ,
money : scene.money ,
score : scene.score ,
waveIndex : scene.currentBattle.waveIndex ,
battleType : scene.currentBattle.battleType ,
trainer : scene.currentBattle.battleType == BattleType . TRAINER ? new TrainerData ( scene . currentBattle . trainer ) : null ,
gameVersion : scene.game.config.gameVersion ,
timestamp : new Date ( ) . getTime ( )
} as SessionSaveData ;
}
2023-12-30 18:41:25 -05:00
saveSession ( scene : BattleScene , skipVerification? : boolean ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
Utils . executeIf ( ! skipVerification , updateUserInfo ) . then ( success = > {
if ( success !== null && ! success )
return resolve ( false ) ;
2024-03-17 11:36:19 -04:00
const sessionData = this . getSessionSaveData ( scene ) ;
2023-12-30 18:41:25 -05:00
2023-12-31 18:30:37 -05:00
if ( ! bypassLogin ) {
2024-04-24 19:26:04 -04:00
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SESSION } &slot= ${ scene . sessionSlotId } ` , JSON . stringify ( sessionData ) , undefined , true )
2023-12-31 18:30:37 -05:00
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error ) {
2024-04-21 10:59:50 -04:00
if ( error . startsWith ( 'session out of date' ) ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
}
2023-12-31 18:30:37 -05:00
console . error ( error ) ;
return resolve ( false ) ;
}
console . debug ( 'Session data saved' ) ;
resolve ( true ) ;
} ) ;
} else {
localStorage . setItem ( 'sessionData' , btoa ( JSON . stringify ( sessionData ) ) ) ;
2023-12-30 18:41:25 -05:00
2023-12-31 18:30:37 -05:00
console . debug ( 'Session data saved' ) ;
resolve ( true ) ;
}
2023-12-30 18:41:25 -05:00
} ) ;
} ) ;
2023-04-28 15:03:42 -04:00
}
2024-03-15 15:13:32 -04:00
getSession ( slotId : integer ) : Promise < SessionSaveData > {
2023-04-28 15:03:42 -04:00
return new Promise ( async ( resolve , reject ) = > {
2024-04-20 18:46:36 -04:00
if ( slotId < 0 )
return resolve ( null ) ;
2023-12-31 18:30:37 -05:00
const handleSessionData = async ( sessionDataStr : string ) = > {
try {
const sessionData = this . parseSessionData ( sessionDataStr ) ;
2024-03-15 15:13:32 -04:00
resolve ( sessionData ) ;
} catch ( err ) {
reject ( err ) ;
return ;
}
} ;
if ( ! bypassLogin ) {
2024-04-19 17:35:49 -04:00
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SESSION } &slot= ${ slotId } ` , true )
2024-03-15 15:13:32 -04:00
. then ( response = > response . text ( ) )
. then ( async response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . error ( response ) ;
return resolve ( null ) ;
}
2023-04-28 15:03:42 -04:00
2024-03-15 15:13:32 -04:00
await handleSessionData ( response ) ;
} ) ;
2024-03-15 15:56:07 -04:00
} else {
const sessionData = localStorage . getItem ( ` sessionData ${ slotId ? slotId : '' } ` ) ;
if ( sessionData )
await handleSessionData ( atob ( sessionData ) ) ;
else
return resolve ( null ) ;
}
2024-03-15 15:13:32 -04:00
} ) ;
}
2024-03-21 13:12:05 -04:00
loadSession ( scene : BattleScene , slotId : integer , sessionData? : SessionSaveData ) : Promise < boolean > {
2024-03-15 15:13:32 -04:00
return new Promise ( async ( resolve , reject ) = > {
try {
2024-03-21 13:12:05 -04:00
const initSessionFromData = async sessionData = > {
2023-12-31 18:30:37 -05:00
console . debug ( sessionData ) ;
2023-04-28 15:03:42 -04:00
2024-03-25 11:00:42 -04:00
scene . gameMode = gameModes [ sessionData . gameMode || GameModes . CLASSIC ] ;
2024-03-15 21:59:34 -04:00
scene . setSeed ( sessionData . seed || scene . game . config . seed [ 0 ] ) ;
2023-12-31 18:30:37 -05:00
scene . resetSeed ( ) ;
2023-04-28 15:03:42 -04:00
2024-03-29 20:11:52 -04:00
console . log ( 'Seed:' , scene . seed ) ;
2024-01-11 20:27:50 -05:00
scene . sessionPlayTime = sessionData . playTime || 0 ;
2023-12-31 18:30:37 -05:00
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
2023-05-31 19:54:57 -04:00
2023-12-31 18:30:37 -05:00
const party = scene . getParty ( ) ;
party . splice ( 0 , party . length ) ;
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
for ( let p of sessionData . party ) {
const pokemon = p . toPokemon ( scene ) as PlayerPokemon ;
pokemon . setVisible ( false ) ;
loadPokemonAssets . push ( pokemon . loadAssets ( ) ) ;
party . push ( pokemon ) ;
}
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
Object . keys ( scene . pokeballCounts ) . forEach ( ( key : string ) = > {
scene . pokeballCounts [ key ] = sessionData . pokeballCounts [ key ] || 0 ;
} ) ;
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
scene . money = sessionData . money || 0 ;
scene . updateMoneyText ( ) ;
2023-04-28 15:03:42 -04:00
2024-01-11 12:26:32 -05:00
if ( scene . money > this . gameStats . highestMoney )
this . gameStats . highestMoney = scene . money ;
2024-03-17 11:36:19 -04:00
scene . score = sessionData . score ;
2024-03-19 00:03:41 -04:00
scene . updateScoreText ( ) ;
2024-03-17 11:36:19 -04:00
2024-04-01 12:48:35 -04:00
scene . newArena ( sessionData . arena . biome ) ;
2023-12-31 18:30:37 -05:00
const battleType = sessionData . battleType || 0 ;
2024-03-21 01:05:46 -04:00
const trainerConfig = sessionData . trainer ? trainerConfigs [ sessionData . trainer . trainerType ] : null ;
const battle = scene . newBattle ( sessionData . waveIndex , battleType , sessionData . trainer , battleType === BattleType . TRAINER ? trainerConfig ? . doubleOnly || sessionData . trainer ? . variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1 ) ;
2024-02-17 09:22:51 -05:00
battle . enemyLevels = sessionData . enemyParty . map ( p = > p . level ) ;
2023-11-10 15:51:34 -05:00
2024-04-01 12:48:35 -04:00
scene . arena . init ( ) ;
2023-10-07 16:08:33 -04:00
2023-12-31 18:30:37 -05:00
sessionData . enemyParty . forEach ( ( enemyData , e ) = > {
2024-03-21 12:34:19 -04:00
const enemyPokemon = enemyData . toPokemon ( scene , battleType , e , sessionData . trainer ? . variant === TrainerVariant . DOUBLE ) as EnemyPokemon ;
2023-12-31 18:30:37 -05:00
battle . enemyParty [ e ] = enemyPokemon ;
if ( battleType === BattleType . WILD )
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2023-10-18 18:01:15 -04:00
2023-12-31 18:30:37 -05:00
loadPokemonAssets . push ( enemyPokemon . loadAssets ( ) ) ;
} ) ;
2023-05-18 11:11:06 -04:00
2023-12-31 18:30:37 -05:00
scene . arena . weather = sessionData . arena . weather ;
// TODO
//scene.arena.tags = sessionData.arena.tags;
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
const modifiersModule = await import ( '../modifier/modifier' ) ;
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
for ( let modifierData of sessionData . modifiers ) {
const modifier = modifierData . toModifier ( scene , modifiersModule [ modifierData . className ] ) ;
if ( modifier )
scene . addModifier ( modifier , true ) ;
}
2023-04-28 15:03:42 -04:00
2023-12-31 18:30:37 -05:00
scene . updateModifiers ( true ) ;
2023-04-28 15:03:42 -04:00
2024-03-15 15:13:32 -04:00
for ( let enemyModifierData of sessionData . enemyModifiers ) {
const modifier = enemyModifierData . toModifier ( scene , modifiersModule [ enemyModifierData . className ] ) ;
if ( modifier )
scene . addEnemyModifier ( modifier , true ) ;
2024-02-17 11:18:53 -05:00
}
2023-04-28 15:03:42 -04:00
2024-03-15 15:13:32 -04:00
scene . updateModifiers ( false ) ;
2023-12-31 18:30:37 -05:00
Promise . all ( loadPokemonAssets ) . then ( ( ) = > resolve ( true ) ) ;
2024-03-21 13:12:05 -04:00
} ;
if ( sessionData )
initSessionFromData ( sessionData ) ;
else {
this . getSession ( slotId )
. then ( data = > initSessionFromData ( data ) )
. catch ( err = > {
reject ( err ) ;
return ;
} ) ;
}
2024-03-15 15:13:32 -04:00
} catch ( err ) {
reject ( err ) ;
return ;
}
2023-04-28 15:03:42 -04:00
} ) ;
}
2024-03-16 22:06:56 -04:00
deleteSession ( slotId : integer ) : Promise < boolean > {
2023-12-31 22:49:50 -05:00
return new Promise < boolean > ( resolve = > {
if ( bypassLogin ) {
localStorage . removeItem ( 'sessionData' ) ;
return resolve ( true ) ;
}
updateUserInfo ( ) . then ( success = > {
if ( success !== null && ! success )
return resolve ( false ) ;
2024-04-19 17:35:49 -04:00
Utils . apiFetch ( ` savedata/delete?datatype= ${ GameDataType . SESSION } &slot= ${ slotId } ` , true ) . then ( response = > {
2023-12-31 22:49:50 -05:00
if ( response . ok ) {
2024-03-15 15:13:32 -04:00
loggedInUser . lastSessionSlot = - 1 ;
2024-04-21 16:19:11 -04:00
resolve ( true ) ;
2023-12-31 22:49:50 -05:00
}
2024-04-21 16:19:11 -04:00
return response . text ( ) ;
} ) . then ( error = > {
if ( error ) {
if ( error . startsWith ( 'session out of date' ) ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
}
console . error ( error ) ;
resolve ( false ) ;
}
resolve ( true ) ;
2023-12-31 22:49:50 -05:00
} ) ;
} ) ;
} ) ;
2023-04-28 15:03:42 -04:00
}
2024-03-17 11:36:19 -04:00
tryClearSession ( scene : BattleScene , slotId : integer ) : Promise < [ success : boolean , newClear : boolean ] > {
2024-03-16 22:06:56 -04:00
return new Promise < [ boolean , boolean ] > ( resolve = > {
if ( bypassLogin ) {
localStorage . removeItem ( 'sessionData' ) ;
return resolve ( [ true , true ] ) ;
}
updateUserInfo ( ) . then ( success = > {
if ( success !== null && ! success )
return resolve ( [ false , false ] ) ;
2024-03-17 11:36:19 -04:00
const sessionData = this . getSessionSaveData ( scene ) ;
2024-04-24 19:26:04 -04:00
Utils . apiPost ( ` savedata/clear?slot= ${ slotId } ` , JSON . stringify ( sessionData ) , undefined , true ) . then ( response = > {
2024-04-21 16:19:11 -04:00
if ( response . ok )
2024-03-16 22:06:56 -04:00
loggedInUser . lastSessionSlot = - 1 ;
2024-04-21 16:19:11 -04:00
return response . json ( ) ;
} ) . then ( jsonResponse = > {
if ( ! jsonResponse . error )
return resolve ( [ true , jsonResponse . success as boolean ] ) ;
if ( jsonResponse && jsonResponse . error . startsWith ( 'session out of date' ) ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
2024-03-16 22:06:56 -04:00
}
2024-04-21 16:19:11 -04:00
console . error ( jsonResponse ) ;
2024-03-16 22:06:56 -04:00
resolve ( [ false , false ] ) ;
2024-04-21 16:19:11 -04:00
} ) ;
2024-03-16 22:06:56 -04:00
} ) ;
} ) ;
}
2023-12-26 14:49:23 -05: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 ) ) ) ;
} * /
2024-01-12 20:16:29 -05:00
if ( k === 'party' || k === 'enemyParty' ) {
2023-12-26 14:49:23 -05:00
const ret : PokemonData [ ] = [ ] ;
2023-12-31 18:30:37 -05:00
if ( v === null )
v = [ ] ;
2023-12-26 14:49:23 -05:00
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 [ ] = [ ] ;
2023-12-31 18:30:37 -05:00
if ( v === null )
v = [ ] ;
2023-12-26 14:49:23 -05:00
for ( let md of v )
ret . push ( new PersistentModifierData ( md , player ) ) ;
return ret ;
}
if ( k === 'arena' )
return new ArenaData ( v ) ;
return v ;
} ) as SessionSaveData ;
}
2024-03-15 15:13:32 -04:00
public tryExportData ( dataType : GameDataType , slotId : integer = 0 ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
const dataKey : string = getDataTypeKey ( dataType , slotId ) ;
const handleData = ( dataStr : string ) = > {
switch ( dataType ) {
case GameDataType . SYSTEM :
dataStr = this . convertSystemDataStr ( dataStr , true ) ;
break ;
}
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 ( ) ;
} ;
if ( ! bypassLogin && dataType < GameDataType . SETTINGS ) {
2024-04-19 17:35:49 -04:00
Utils . apiFetch ( ` savedata/get?datatype= ${ dataType } ${ dataType === GameDataType . SESSION ? ` &slot= ${ slotId } ` : '' } ` , true )
2024-03-15 15:13:32 -04:00
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . error ( response ) ;
resolve ( false ) ;
return ;
}
2023-12-31 18:30:37 -05:00
2024-03-15 15:13:32 -04:00
handleData ( response ) ;
resolve ( true ) ;
} ) ;
} else {
const data = localStorage . getItem ( dataKey ) ;
if ( data )
handleData ( atob ( data ) ) ;
resolve ( ! ! data ) ;
}
} ) ;
2023-12-26 14:49:23 -05:00
}
2024-03-15 15:13:32 -04:00
public importData ( dataType : GameDataType , slotId : integer = 0 ) : void {
const dataKey = getDataTypeKey ( dataType , slotId ) ;
2023-12-26 14:49:23 -05:00
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 = > {
2023-12-31 13:39:04 -05:00
let dataStr = AES . decrypt ( e . target . result . toString ( ) , saveKey ) . toString ( enc . Utf8 ) ;
2023-12-26 14:49:23 -05:00
let valid = false ;
try {
switch ( dataType ) {
case GameDataType . SYSTEM :
2023-12-31 13:39:04 -05:00
dataStr = this . convertSystemDataStr ( dataStr ) ;
2023-12-26 14:49:23 -05:00
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 :
2024-02-13 18:42:11 -05:00
case GameDataType . TUTORIALS :
2023-12-26 14:49:23 -05:00
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 ;
2024-02-13 18:42:11 -05:00
case GameDataType . TUTORIALS :
dataName = 'tutorials' ;
break ;
2023-12-26 14:49:23 -05:00
}
2023-12-31 18:30:37 -05:00
const displayError = ( error : string ) = > this . scene . ui . showText ( error , null , ( ) = > this . scene . ui . showText ( null , 0 ) , Utils . fixedInt ( 1500 ) ) ;
2023-12-26 14:49:23 -05:00
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 , ( ) = > {
2024-02-13 18:42:11 -05:00
if ( ! bypassLogin && dataType < GameDataType . SETTINGS ) {
2023-12-31 18:30:37 -05:00
updateUserInfo ( ) . then ( success = > {
if ( ! success )
return displayError ( ` Could not contact the server. Your ${ dataName } data could not be imported. ` ) ;
2024-04-24 19:26:04 -04:00
Utils . apiPost ( ` savedata/update?datatype= ${ dataType } ${ dataType === GameDataType . SESSION ? ` &slot= ${ slotId } ` : '' } ` , dataStr , undefined , true )
2023-12-31 18:30:37 -05:00
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error ) {
console . error ( error ) ;
return displayError ( ` An error occurred while updating ${ dataName } data. Please contact the administrator. ` ) ;
}
window . location = window . location ;
} ) ;
} ) ;
} else {
localStorage . setItem ( dataKey , btoa ( dataStr ) ) ;
window . location = window . location ;
}
2023-12-26 14:49:23 -05:00
} , ( ) = > {
this . scene . ui . revertMode ( ) ;
this . scene . ui . showText ( null , 0 ) ;
2024-02-06 23:11:00 -05:00
} , false , - 98 ) ;
2023-12-26 14:49:23 -05:00
} ) ;
} ;
} ) ( ( 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 15:03:42 -04:00
private initDexData ( ) : void {
2023-04-17 22:44:41 -04:00
const data : DexData = { } ;
for ( let species of allSpecies ) {
2023-11-12 23:47:04 -05:00
data [ species . speciesId ] = {
2024-01-05 22:24:05 -05:00
seenAttr : 0n , caughtAttr : 0n , natureAttr : 0 , seenCount : 0 , caughtCount : 0 , hatchedCount : 0 , ivs : [ 0 , 0 , 0 , 0 , 0 , 0 ]
2023-11-12 23:47:04 -05:00
} ;
2023-04-17 22:44:41 -04:00
}
2024-04-18 22:52:26 -04:00
const defaultStarterAttr = DexAttr . NON_SHINY | DexAttr . MALE | DexAttr . DEFAULT_VARIANT | DexAttr . DEFAULT_FORM ;
2023-11-12 23:47:04 -05:00
2024-01-05 22:24:05 -05:00
const defaultStarterNatures : Nature [ ] = [ ] ;
this . scene . executeWithSeedOffset ( ( ) = > {
const neutralNatures = [ Nature . HARDY , Nature . DOCILE , Nature . SERIOUS , Nature . BASHFUL , Nature . QUIRKY ] ;
2024-04-19 23:37:23 -04:00
for ( let s = 0 ; s < defaultStarterSpecies . length ; s ++ )
2024-02-17 00:40:03 -05:00
defaultStarterNatures . push ( Utils . randSeedItem ( neutralNatures ) ) ;
2024-01-05 22:24:05 -05:00
} , 0 , 'default' ) ;
2024-04-19 23:37:23 -04:00
for ( let ds = 0 ; ds < defaultStarterSpecies . length ; ds ++ ) {
let entry = data [ defaultStarterSpecies [ ds ] ] as DexEntry ;
2023-11-12 23:47:04 -05:00
entry . seenAttr = defaultStarterAttr ;
entry . caughtAttr = defaultStarterAttr ;
2024-01-05 22:24:05 -05:00
entry . natureAttr = Math . pow ( 2 , defaultStarterNatures [ ds ] + 1 ) ;
2023-11-12 23:47:04 -05:00
for ( let i in entry . ivs )
entry . ivs [ i ] = 10 ;
2023-04-18 01:32:26 -04:00
}
2023-04-17 22:44:41 -04:00
2024-01-05 22:24:05 -05:00
this . defaultDexData = Object . assign ( { } , data ) ;
2023-04-18 01:32:26 -04:00
this . dexData = data ;
}
2024-04-13 18:59:58 -04:00
private initStarterData ( ) : void {
const starterData : StarterData = { } ;
2024-02-25 12:45:41 -05:00
2024-04-13 18:59:58 -04:00
const starterSpeciesIds = Object . keys ( speciesStarters ) . map ( k = > parseInt ( k ) as Species ) ;
2024-03-01 19:37:32 -05:00
2024-04-13 18:59:58 -04:00
for ( let speciesId of starterSpeciesIds ) {
starterData [ speciesId ] = {
moveset : null ,
eggMoves : 0 ,
candyCount : 0 ,
2024-04-23 22:32:04 -04:00
abilityAttr : defaultStarterSpecies.includes ( speciesId ) ? AbilityAttr.ABILITY_1 : 0 ,
2024-04-13 18:59:58 -04:00
passiveAttr : 0 ,
valueReduction : 0
} ;
}
this . starterData = starterData ;
2024-02-25 12:45:41 -05:00
}
2024-04-22 11:28:13 -04:00
setPokemonSeen ( pokemon : Pokemon , incrementCount : boolean = true , trainer : boolean = false ) : void {
2023-11-12 23:47:04 -05:00
const dexEntry = this . dexData [ pokemon . species . speciesId ] ;
2023-11-13 08:20:31 -05:00
dexEntry . seenAttr |= pokemon . getDexAttr ( ) ;
2024-01-11 12:26:32 -05:00
if ( incrementCount ) {
2023-11-12 23:47:04 -05:00
dexEntry . seenCount ++ ;
2024-01-11 12:26:32 -05:00
this . gameStats . pokemonSeen ++ ;
2024-04-22 11:28:13 -04:00
if ( ! trainer && pokemon . isShiny ( ) )
2024-01-11 12:26:32 -05:00
this . gameStats . shinyPokemonSeen ++ ;
}
2023-04-18 01:32:26 -04:00
}
2023-04-17 22:44:41 -04:00
2023-12-31 13:20:28 -05:00
setPokemonCaught ( pokemon : Pokemon , incrementCount : boolean = true , fromEgg : boolean = false ) : Promise < void > {
return this . setPokemonSpeciesCaught ( pokemon , pokemon . species , incrementCount , fromEgg ) ;
2023-07-05 14:19:49 -04:00
}
2023-12-31 13:20:28 -05:00
setPokemonSpeciesCaught ( pokemon : Pokemon , species : PokemonSpecies , incrementCount : boolean = true , fromEgg : boolean = false ) : Promise < void > {
2024-02-25 12:45:41 -05:00
return new Promise < void > ( resolve = > {
2023-11-12 23:47:04 -05:00
const dexEntry = this . dexData [ species . speciesId ] ;
const caughtAttr = dexEntry . caughtAttr ;
2024-04-05 22:58:40 -04:00
const formIndex = pokemon . formIndex ;
if ( noStarterFormKeys . includes ( pokemon . getFormKey ( ) ) )
pokemon . formIndex = 0 ;
const dexAttr = pokemon . getDexAttr ( ) ;
pokemon . formIndex = formIndex ;
dexEntry . caughtAttr |= dexAttr ;
2024-04-19 11:01:22 -04:00
if ( speciesStarters . hasOwnProperty ( species . speciesId ) ) {
this . starterData [ species . speciesId ] . abilityAttr |= pokemon . abilityIndex !== 1 || pokemon . species . ability2
? Math . pow ( 2 , pokemon . abilityIndex )
: AbilityAttr . ABILITY_HIDDEN ;
}
2024-01-05 22:24:05 -05:00
dexEntry . natureAttr |= Math . pow ( 2 , pokemon . nature + 1 ) ;
2024-04-13 18:59:58 -04:00
const hasPrevolution = pokemonPrevolutions . hasOwnProperty ( species . speciesId ) ;
const newCatch = ! caughtAttr ;
2023-12-31 13:20:28 -05:00
if ( incrementCount ) {
2024-01-11 12:26:32 -05:00
if ( ! fromEgg ) {
2023-12-31 13:20:28 -05:00
dexEntry . caughtCount ++ ;
2024-01-11 12:26:32 -05:00
this . gameStats . pokemonCaught ++ ;
if ( pokemon . species . pseudoLegendary || pokemon . species . legendary )
this . gameStats . legendaryPokemonCaught ++ ;
else if ( pokemon . species . mythical )
this . gameStats . mythicalPokemonCaught ++ ;
if ( pokemon . isShiny ( ) )
this . gameStats . shinyPokemonCaught ++ ;
} else {
2023-12-31 13:20:28 -05:00
dexEntry . hatchedCount ++ ;
2024-01-11 12:26:32 -05:00
this . gameStats . pokemonHatched ++ ;
if ( pokemon . species . pseudoLegendary || pokemon . species . legendary )
this . gameStats . legendaryPokemonHatched ++ ;
else if ( pokemon . species . mythical )
this . gameStats . mythicalPokemonHatched ++ ;
if ( pokemon . isShiny ( ) )
this . gameStats . shinyPokemonHatched ++ ;
}
2023-04-18 01:32:26 -04:00
2024-04-13 18:59:58 -04:00
if ( ! hasPrevolution )
2024-04-19 01:01:57 -04:00
this . addStarterCandy ( species , ( 1 * ( pokemon . isShiny ( ) ? 5 * Math . pow ( 2 , pokemon . variant || 0 ) : 1 ) ) * ( fromEgg || pokemon . isBoss ( ) ? 2 : 1 ) ) ;
2024-04-13 18:59:58 -04:00
}
2023-12-01 17:23:26 -05:00
const checkPrevolution = ( ) = > {
if ( hasPrevolution ) {
const prevolutionSpecies = pokemonPrevolutions [ species . speciesId ] ;
2023-12-31 13:20:28 -05:00
return this . setPokemonSpeciesCaught ( pokemon , getPokemonSpecies ( prevolutionSpecies ) , incrementCount , fromEgg ) . then ( ( ) = > resolve ( ) ) ;
2023-12-01 17:23:26 -05:00
} else
resolve ( ) ;
} ;
2023-04-18 01:32:26 -04:00
2023-12-01 17:23:26 -05:00
if ( newCatch && speciesStarters . hasOwnProperty ( species . speciesId ) ) {
2024-04-04 17:43:37 -04:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2023-12-01 17:23:26 -05:00
this . scene . ui . showText ( ` ${ species . name } has been \ nadded as a starter! ` , null , ( ) = > checkPrevolution ( ) , null , true ) ;
2023-07-05 14:19:49 -04:00
} else
2023-12-01 17:23:26 -05:00
checkPrevolution ( ) ;
2023-04-18 01:32:26 -04:00
} ) ;
}
2024-04-13 18:59:58 -04:00
addStarterCandy ( species : PokemonSpecies , count : integer ) : void {
this . scene . candyBar . showStarterSpeciesCandy ( species . speciesId , count ) ;
this . starterData [ species . speciesId ] . candyCount += count ;
}
2024-02-25 12:45:41 -05:00
setEggMoveUnlocked ( species : PokemonSpecies , eggMoveIndex : integer ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
const speciesId = species . speciesId ;
if ( ! speciesEggMoves . hasOwnProperty ( speciesId ) || ! speciesEggMoves [ speciesId ] [ eggMoveIndex ] ) {
resolve ( false ) ;
return ;
}
2024-04-13 18:59:58 -04:00
if ( ! this . starterData [ speciesId ] . eggMoves )
this . starterData [ speciesId ] . eggMoves = 0 ;
2024-02-25 12:45:41 -05:00
const value = Math . pow ( 2 , eggMoveIndex ) ;
2024-04-13 18:59:58 -04:00
if ( this . starterData [ speciesId ] . eggMoves & value ) {
2024-02-25 12:45:41 -05:00
resolve ( false ) ;
return ;
}
2024-04-13 18:59:58 -04:00
this . starterData [ speciesId ] . eggMoves |= value ;
2024-02-25 12:45:41 -05:00
2024-04-04 17:43:37 -04:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2024-02-25 12:45:41 -05:00
this . scene . ui . showText ( ` ${ eggMoveIndex === 3 ? 'Rare ' : '' } Egg Move unlocked: ${ allMoves [ speciesEggMoves [ speciesId ] [ eggMoveIndex ] ] . name } ` , null , ( ) = > resolve ( true ) , null , true ) ;
} ) ;
}
2023-12-19 23:51:48 -05: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 ] ) ) ;
}
2024-04-05 22:58:40 -04:00
getSpeciesCount ( dexEntryPredicate : ( entry : DexEntry ) = > boolean ) : integer {
const dexKeys = Object . keys ( this . dexData ) ;
let speciesCount = 0 ;
for ( let s of dexKeys ) {
if ( dexEntryPredicate ( this . dexData [ s ] ) )
speciesCount ++ ;
}
return speciesCount ;
}
getStarterCount ( dexEntryPredicate : ( entry : DexEntry ) = > boolean ) : integer {
const starterKeys = Object . keys ( speciesStarters ) ;
let starterCount = 0 ;
for ( let s of starterKeys ) {
const starterDexEntry = this . dexData [ s ] ;
if ( dexEntryPredicate ( starterDexEntry ) )
starterCount ++ ;
}
return starterCount ;
}
2024-04-18 22:52:26 -04:00
getSpeciesDefaultDexAttr ( species : PokemonSpecies , forSeen : boolean = false , optimistic : boolean = false ) : bigint {
2023-11-12 23:47:04 -05:00
let ret = 0 n ;
const dexEntry = this . dexData [ species . speciesId ] ;
const attr = dexEntry . caughtAttr ;
2024-04-18 22:52:26 -04:00
ret |= optimistic
? attr & DexAttr . SHINY ? DexAttr.SHINY : DexAttr.NON_SHINY
: attr & DexAttr . NON_SHINY || ! ( attr & DexAttr . SHINY ) ? DexAttr.NON_SHINY : DexAttr.SHINY ;
2023-11-12 23:47:04 -05:00
ret |= attr & DexAttr . MALE || ! ( attr & DexAttr . FEMALE ) ? DexAttr.MALE : DexAttr.FEMALE ;
2024-04-18 22:52:26 -04:00
ret |= optimistic
? attr & DexAttr . SHINY ? attr & DexAttr . VARIANT_3 ? DexAttr.VARIANT_3 : attr & DexAttr . VARIANT_2 ? DexAttr.VARIANT_2 : DexAttr.DEFAULT_VARIANT : DexAttr . DEFAULT_VARIANT
: attr & DexAttr . DEFAULT_VARIANT ? DexAttr.DEFAULT_VARIANT : attr & DexAttr . VARIANT_2 ? DexAttr.VARIANT_2 : attr & DexAttr . VARIANT_3 ? DexAttr.VARIANT_3 : DexAttr.DEFAULT_VARIANT ;
2023-11-12 23:47:04 -05:00
ret |= this . getFormAttr ( this . getFormIndex ( attr ) ) ;
return ret ;
2023-04-17 22:44:41 -04:00
}
2023-11-12 23:47:04 -05:00
getSpeciesDexAttrProps ( species : PokemonSpecies , dexAttr : bigint ) : DexAttrProps {
const shiny = ! ( dexAttr & DexAttr . NON_SHINY ) ;
const female = ! ( dexAttr & DexAttr . MALE ) ;
2024-04-18 22:52:26 -04:00
const variant = dexAttr & DexAttr . DEFAULT_VARIANT ? 0 : dexAttr & DexAttr . VARIANT_2 ? 1 : dexAttr & DexAttr . VARIANT_3 ? 2 : 0 ;
2023-11-12 23:47:04 -05:00
const formIndex = this . getFormIndex ( dexAttr ) ;
return {
shiny ,
female ,
2024-04-18 22:52:26 -04:00
variant ,
2023-11-12 23:47:04 -05:00
formIndex
2023-04-17 22:44:41 -04:00
} ;
2023-11-12 23:47:04 -05:00
}
2023-04-17 22:44:41 -04:00
2024-04-18 22:52:26 -04:00
getStarterSpeciesDefaultAbilityIndex ( species : PokemonSpecies ) : integer {
const abilityAttr = this . starterData [ species . speciesId ] . abilityAttr ;
return abilityAttr & AbilityAttr . ABILITY_1 ? 0 : ! species . ability2 || abilityAttr & AbilityAttr . ABILITY_2 ? 1 : 2 ;
}
2024-01-05 22:24:05 -05:00
getSpeciesDefaultNature ( species : PokemonSpecies ) : Nature {
const dexEntry = this . dexData [ species . speciesId ] ;
for ( let n = 0 ; n < 25 ; n ++ ) {
if ( dexEntry . natureAttr & Math . pow ( 2 , n + 1 ) )
return n as Nature ;
}
return 0 as Nature ;
}
getSpeciesDefaultNatureAttr ( species : PokemonSpecies ) : integer {
return Math . pow ( 2 , this . getSpeciesDefaultNature ( species ) ) ;
}
getNaturesForAttr ( natureAttr : integer ) : Nature [ ] {
let ret : Nature [ ] = [ ] ;
for ( let n = 0 ; n < 25 ; n ++ ) {
if ( natureAttr & Math . pow ( 2 , n + 1 ) )
ret . push ( n ) ;
}
return ret ;
}
2024-02-12 16:38:46 -05:00
getSpeciesStarterValue ( speciesId : Species ) : number {
const baseValue = speciesStarters [ speciesId ] ;
let value = baseValue ;
const decrementValue = ( value : number ) = > {
if ( value > 1 )
value -- ;
else
value /= 2 ;
return value ;
}
2024-04-13 18:59:58 -04:00
for ( let v = 0 ; v < this . starterData [ speciesId ] . valueReduction ; v ++ )
2024-02-12 16:38:46 -05:00
value = decrementValue ( value ) ;
return value ;
}
2023-11-12 23:47:04 -05: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 22:44:41 -04:00
2023-11-12 23:47:04 -05:00
getFormAttr ( formIndex : integer ) : bigint {
return BigInt ( Math . pow ( 2 , 7 + formIndex ) ) ;
2023-04-17 22:44:41 -04:00
}
2023-12-31 13:20:28 -05:00
consolidateDexData ( dexData : DexData ) : void {
for ( let k of Object . keys ( dexData ) ) {
const entry = dexData [ k ] as DexEntry ;
if ( ! entry . hasOwnProperty ( 'hatchedCount' ) )
entry . hatchedCount = 0 ;
2024-01-05 22:24:05 -05:00
if ( ! entry . hasOwnProperty ( 'natureAttr' ) || ( entry . caughtAttr && ! entry . natureAttr ) )
entry . natureAttr = this . defaultDexData [ k ] . natureAttr || Math . pow ( 2 , Utils . randInt ( 25 , 1 ) ) ;
2023-04-26 12:50:21 -04:00
}
}
2024-04-18 22:52:26 -04:00
2024-04-21 10:59:50 -04:00
migrateStarterAbilities ( systemData : SystemSaveData , initialStarterData? : StarterData ) : void {
2024-04-18 22:52:26 -04:00
const starterIds = Object . keys ( this . starterData ) . map ( s = > parseInt ( s ) as Species ) ;
2024-04-21 10:59:50 -04:00
const starterData = initialStarterData || systemData . starterData ;
2024-04-18 22:52:26 -04:00
const dexData = systemData . dexData ;
for ( let s of starterIds ) {
const dexAttr = dexData [ s ] . caughtAttr ;
starterData [ s ] . abilityAttr = ( dexAttr & DexAttr . DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0 )
| ( dexAttr & DexAttr . VARIANT_2 ? AbilityAttr.ABILITY_2 : 0 )
| ( dexAttr & DexAttr . VARIANT_3 ? AbilityAttr.ABILITY_HIDDEN : 0 ) ;
if ( dexAttr ) {
if ( ! ( dexAttr & DexAttr . DEFAULT_VARIANT ) )
dexData [ s ] . caughtAttr ^= DexAttr . DEFAULT_VARIANT ;
if ( dexAttr & DexAttr . VARIANT_2 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
if ( dexAttr & DexAttr . VARIANT_3 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
}
}
}
2024-04-20 22:30:28 -04:00
fixVariantData ( systemData : SystemSaveData ) : void {
const starterIds = Object . keys ( this . starterData ) . map ( s = > parseInt ( s ) as Species ) ;
const starterData = systemData . starterData ;
const dexData = systemData . dexData ;
if ( starterIds . find ( id = > ( dexData [ id ] . caughtAttr & DexAttr . VARIANT_2 || dexData [ id ] . caughtAttr & DexAttr . VARIANT_3 ) && ! variantData [ id ] ) ) {
for ( let s of starterIds ) {
const species = getPokemonSpecies ( s ) ;
if ( variantData [ s ] ) {
const tempCaughtAttr = dexData [ s ] . caughtAttr ;
let seenVariant2 = false ;
let seenVariant3 = false ;
let checkEvoSpecies = ( es : Species ) = > {
seenVariant2 || = ! ! ( dexData [ es ] . seenAttr & DexAttr . VARIANT_2 ) ;
seenVariant3 || = ! ! ( dexData [ es ] . seenAttr & DexAttr . VARIANT_3 ) ;
if ( pokemonEvolutions . hasOwnProperty ( es ) ) {
for ( let pe of pokemonEvolutions [ es ] )
checkEvoSpecies ( pe . speciesId ) ;
}
} ;
checkEvoSpecies ( s ) ;
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_2 && ! seenVariant2 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_3 && ! seenVariant3 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
starterData [ s ] . abilityAttr = ( tempCaughtAttr & DexAttr . DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0 )
| ( tempCaughtAttr & DexAttr . VARIANT_2 && species . ability2 ? AbilityAttr.ABILITY_2 : 0 )
| ( tempCaughtAttr & DexAttr . VARIANT_3 && species . abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0 ) ;
} else {
const tempCaughtAttr = dexData [ s ] . caughtAttr ;
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_2 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_3 )
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
starterData [ s ] . abilityAttr = ( tempCaughtAttr & DexAttr . DEFAULT_VARIANT ? AbilityAttr.ABILITY_1 : 0 )
| ( tempCaughtAttr & DexAttr . VARIANT_2 && species . ability2 ? AbilityAttr.ABILITY_2 : 0 )
| ( tempCaughtAttr & DexAttr . VARIANT_3 && species . abilityHidden ? AbilityAttr.ABILITY_HIDDEN : 0 ) ;
}
}
}
}
2024-04-23 22:16:49 -04:00
fixStarterData ( systemData : SystemSaveData ) : void {
for ( let starterId of defaultStarterSpecies )
systemData . starterData [ starterId ] . abilityAttr |= AbilityAttr . ABILITY_1 ;
}
2023-04-17 22:44:41 -04:00
}