2023-12-31 15:30:37 -08:00
import BattleScene , { PokeballCounts , bypassLogin } from "../battle-scene" ;
2024-02-29 17:08:50 -08:00
import Pokemon , { EnemyPokemon , PlayerPokemon } from "../field/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" ;
2024-01-13 09:24:24 -08:00
import { Species } from "../data/enums/species" ;
2023-04-20 12:46:05 -07:00
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" ;
2024-03-14 13:26:57 -07:00
import { GameModes , gameModes } from "../game-mode" ;
2023-10-07 13:08:33 -07:00
import { BattleType } from "../battle" ;
import TrainerData from "./trainer-data" ;
2024-01-13 09:24:24 -08:00
import { trainerConfigs } from "../data/trainer-config" ;
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" ;
2023-12-31 19:49:50 -08:00
import { loggedInUser , updateUserInfo } from "../account" ;
2024-01-05 19:24:05 -08:00
import { Nature } from "../data/nature" ;
2024-01-11 09:26:32 -08:00
import { GameStats } from "./game-stats" ;
2024-02-13 15:42:11 -08:00
import { Tutorial } from "../tutorial" ;
2024-02-20 22:03:34 -08:00
import { Moves } from "../data/enums/moves" ;
2024-02-25 09:45:41 -08:00
import { speciesEggMoves } from "../data/egg-moves" ;
import { allMoves } from "../data/move" ;
2024-03-20 21:57:28 -07:00
import { TrainerVariant } from "../field/trainer" ;
2023-12-26 11:49:23 -08:00
const saveKey = 'x0i2O7WRiANTqPmZ' ; // Temporary; secure encryption is not yet necessary
export enum GameDataType {
SYSTEM ,
SESSION ,
2024-02-13 15:42:11 -08:00
SETTINGS ,
TUTORIALS
2023-12-26 11:49:23 -08:00
}
2024-02-06 13:15:35 -08:00
export enum PlayerGender {
UNSET ,
MALE ,
FEMALE
}
2024-03-15 12:13:32 -07:00
export function getDataTypeKey ( dataType : GameDataType , slotId : integer = 0 ) : string {
2023-12-26 11:49:23 -08:00
switch ( dataType ) {
case GameDataType . SYSTEM :
return 'data' ;
case GameDataType . SESSION :
2024-03-15 12:13:32 -07:00
let ret = 'sessionData' ;
if ( slotId )
ret += slotId ;
return ret ;
2023-12-26 11:49:23 -08:00
case GameDataType . SETTINGS :
return 'settings' ;
2024-02-13 15:42:11 -08:00
case GameDataType . TUTORIALS :
return 'tutorials' ;
2023-12-26 11:49:23 -08:00
}
}
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 ;
2024-02-06 13:15:35 -08:00
gender : PlayerGender ;
2023-04-17 22:32:26 -07:00
dexData : DexData ;
2024-02-20 22:03:34 -08:00
starterMoveData : StarterMoveData ;
2024-02-25 09:45:41 -08:00
starterEggMoveData : StarterEggMoveData ;
2024-01-11 09:26:32 -08:00
gameStats : GameStats ;
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 ;
}
2024-03-15 12:13:32 -07:00
export interface SessionSaveData {
2023-10-18 15:01:15 -07:00
seed : string ;
2024-01-11 17:27:50 -08:00
playTime : integer ;
2024-03-14 13:26:57 -07:00
gameMode : GameModes ;
2023-04-28 12:03:42 -07:00
party : PokemonData [ ] ;
2023-10-07 13:08:33 -07:00
enemyParty : 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 ;
2024-03-17 08:36:19 -07:00
score : 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 ;
2024-01-05 19:24:05 -08:00
natureAttr : integer ,
2023-11-12 20:47:04 -08:00
seenCount : integer ;
caughtCount : integer ;
2023-12-31 10:20:28 -08:00
hatchedCount : integer ;
2023-11-12 20:47:04 -08:00
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
}
2024-02-20 22:03:34 -08:00
export type StarterMoveset = [ Moves ] | [ Moves , Moves ] | [ Moves , Moves , Moves ] | [ Moves , Moves , Moves , Moves ] ;
export interface StarterMoveData {
[ key : integer ] : StarterMoveset | StarterFormMoveData
}
export interface StarterFormMoveData {
[ key : integer ] : StarterMoveset
}
2024-02-25 09:45:41 -08:00
export interface StarterEggMoveData {
[ key : integer ] : integer
}
2024-02-13 15:42:11 -08:00
export interface TutorialFlags {
[ key : string ] : boolean
}
2023-12-31 10:39:04 -08:00
const systemShortKeys = {
seenAttr : '$sa' ,
caughtAttr : '$ca' ,
2024-01-05 19:24:05 -08:00
natureAttr : '$na' ,
2023-12-31 10:39:04 -08:00
seenCount : '$s' ,
caughtCount : '$c' ,
ivs : '$i'
} ;
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 ;
2024-02-06 13:15:35 -08:00
public gender : PlayerGender ;
2023-04-17 19:44:41 -07:00
public dexData : DexData ;
2024-01-05 19:24:05 -08:00
private defaultDexData : DexData ;
2023-04-17 19:44:41 -07:00
2024-02-20 22:03:34 -08:00
public starterMoveData : StarterMoveData ;
2024-02-25 09:45:41 -08:00
public starterEggMoveData : StarterEggMoveData ;
2024-01-11 09:26:32 -08:00
public gameStats : GameStats ;
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 ) ;
2024-02-20 22:03:34 -08:00
this . starterMoveData = { } ;
2024-02-25 09:45:41 -08:00
this . starterEggMoveData = { } ;
2024-01-11 09:26:32 -08:00
this . gameStats = new GameStats ( ) ;
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 ( ) ;
2024-02-25 09:45:41 -08:00
this . initEggMoveData ( ) ;
2023-04-17 19:44:41 -07:00
}
2023-12-30 15:41:25 -08:00
public saveSystem ( ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
2024-01-11 09:26:32 -08:00
updateUserInfo ( ) . then ( ( success : boolean ) = > {
2023-12-30 15:41:25 -08:00
if ( ! success )
return resolve ( false ) ;
const data : SystemSaveData = {
trainerId : this.trainerId ,
secretId : this.secretId ,
2024-02-06 13:15:35 -08:00
gender : this.gender ,
2023-12-30 15:41:25 -08:00
dexData : this.dexData ,
2024-02-20 22:03:34 -08:00
starterMoveData : this.starterMoveData ,
2024-02-25 09:45:41 -08:00
starterEggMoveData : this.starterEggMoveData ,
2024-01-11 09:26:32 -08:00
gameStats : this.gameStats ,
2023-12-30 15:41:25 -08: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 15:30:37 -08:00
2023-12-30 15:41:25 -08:00
const maxIntAttrValue = Math . pow ( 2 , 31 ) ;
2023-12-31 15:30:37 -08:00
const systemData = JSON . stringify ( data , ( k : any , v : any ) = > typeof v === 'bigint' ? v <= maxIntAttrValue ? Number ( v ) : v . toString ( ) : v ) ;
if ( ! bypassLogin ) {
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SYSTEM } ` , systemData )
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error ) {
console . error ( error ) ;
return resolve ( false ) ;
}
resolve ( true ) ;
} ) ;
} else {
localStorage . setItem ( 'data_bak' , localStorage . getItem ( 'data' ) ) ;
localStorage . setItem ( 'data' , btoa ( systemData ) ) ;
2024-01-10 11:02:43 -08:00
resolve ( true ) ;
2023-12-31 15:30:37 -08:00
}
2023-12-30 15:41:25 -08:00
} ) ;
} ) ;
2023-04-17 19:44:41 -07:00
}
2024-02-03 21:49:57 -08:00
public loadSystem ( ) : Promise < boolean > {
2023-12-31 15:30:37 -08:00
return new Promise < boolean > ( resolve = > {
if ( bypassLogin && ! localStorage . hasOwnProperty ( 'data' ) )
2024-02-19 17:36:10 -08:00
return resolve ( false ) ;
2023-04-17 22:32:26 -07:00
2023-12-31 15:30:37 -08:00
const handleSystemData = ( systemDataStr : string ) = > {
2024-02-19 17:36:10 -08:00
try {
const systemData = this . parseSystemData ( systemDataStr ) ;
2023-11-12 20:47:04 -08:00
2024-02-19 17:36:10 -08:00
console . debug ( systemData ) ;
2023-04-17 22:32:26 -07:00
2024-02-19 17:36:10 -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-12-13 21:41:35 -08:00
2024-02-19 17:36:10 -08:00
this . trainerId = systemData . trainerId ;
this . secretId = systemData . secretId ;
2023-04-17 19:44:41 -07:00
2024-02-19 17:36:10 -08:00
this . gender = systemData . gender ;
2024-02-06 13:15:35 -08:00
2024-02-19 17:36:10 -08:00
this . saveSetting ( Setting . Player_Gender , systemData . gender === PlayerGender . FEMALE ? 1 : 0 ) ;
2024-02-06 13:15:35 -08:00
2024-02-20 22:03:34 -08:00
this . starterMoveData = systemData . starterMoveData || { } ;
2024-02-25 09:45:41 -08:00
2024-03-01 09:41:47 -08:00
this . starterEggMoveData = { } ;
this . initEggMoveData ( ) ;
if ( systemData . starterEggMoveData ) {
for ( let key of Object . keys ( systemData . starterEggMoveData ) ) {
if ( this . starterEggMoveData . hasOwnProperty ( key ) )
this . starterEggMoveData [ key ] = systemData . starterEggMoveData [ key ] ;
}
2024-02-25 09:45:41 -08:00
}
2024-02-20 22:03:34 -08:00
2024-02-19 17:36:10 -08:00
if ( systemData . gameStats )
this . gameStats = systemData . gameStats ;
2024-01-11 09:26:32 -08:00
2024-02-19 17:36:10 -08: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 15:30:37 -08:00
}
2023-12-19 20:51:48 -08:00
2024-02-19 17:36:10 -08: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 15:30:37 -08:00
2024-02-19 17:36:10 -08: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 15:30:37 -08:00
}
2023-12-19 20:51:48 -08:00
2024-02-19 17:36:10 -08:00
if ( systemData . voucherCounts ) {
Utils . getEnumKeys ( VoucherType ) . forEach ( key = > {
const index = VoucherType [ key ] ;
this . voucherCounts [ index ] = systemData . voucherCounts [ index ] || 0 ;
} ) ;
}
2023-12-15 20:07:32 -08:00
2024-02-19 17:36:10 -08:00
this . eggs = systemData . eggs
? systemData . eggs . map ( e = > e . toEgg ( ) )
: [ ] ;
2023-04-26 13:07:29 -07:00
2024-02-19 17:36:10 -08:00
this . dexData = Object . assign ( this . dexData , systemData . dexData ) ;
this . consolidateDexData ( this . dexData ) ;
this . defaultDexData = null ;
2023-12-31 15:30:37 -08:00
2024-02-19 17:36:10 -08:00
resolve ( true ) ;
} catch ( err ) {
console . error ( err ) ;
resolve ( false ) ;
}
2023-12-31 15:30:37 -08:00
}
if ( ! bypassLogin ) {
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SYSTEM } ` )
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . error ( response ) ;
return resolve ( false ) ;
}
handleSystemData ( response ) ;
} ) ;
} else
handleSystemData ( atob ( localStorage . getItem ( 'data' ) ) ) ;
} ) ;
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 ) = > {
2024-01-11 09:26:32 -08:00
if ( k === 'gameStats' )
return new GameStats ( v ) ;
else if ( k === 'eggs' ) {
2023-12-26 11:49:23 -08:00
const ret : EggData [ ] = [ ] ;
2023-12-31 15:53:16 -08:00
if ( v === null )
v = [ ] ;
2023-12-26 11:49:23 -08:00
for ( let e of v )
ret . push ( new EggData ( e ) ) ;
return ret ;
}
2024-01-05 19:24:05 -08:00
return k . endsWith ( 'Attr' ) && k !== 'natureAttr' ? BigInt ( v ) : v ;
2023-12-26 11:49:23 -08:00
} ) as SystemSaveData ;
}
2023-12-31 10:39:04 -08: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 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 ] ) ;
}
2024-02-13 15:42:11 -08: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 08:36:19 -07: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 15:41:25 -08: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 08:36:19 -07:00
const sessionData = this . getSessionSaveData ( scene ) ;
2023-12-30 15:41:25 -08:00
2023-12-31 15:30:37 -08:00
if ( ! bypassLogin ) {
2024-03-14 18:49:49 -07:00
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SESSION } &slot= ${ scene . sessionSlotId } ` , JSON . stringify ( sessionData ) )
2023-12-31 15:30:37 -08:00
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error ) {
console . error ( error ) ;
return resolve ( false ) ;
}
console . debug ( 'Session data saved' ) ;
resolve ( true ) ;
} ) ;
} else {
localStorage . setItem ( 'sessionData' , btoa ( JSON . stringify ( sessionData ) ) ) ;
2023-12-30 15:41:25 -08:00
2023-12-31 15:30:37 -08:00
console . debug ( 'Session data saved' ) ;
resolve ( true ) ;
}
2023-12-30 15:41:25 -08:00
} ) ;
} ) ;
2023-04-28 12:03:42 -07:00
}
2024-03-15 12:13:32 -07:00
getSession ( slotId : integer ) : Promise < SessionSaveData > {
2023-04-28 12:03:42 -07:00
return new Promise ( async ( resolve , reject ) = > {
2023-12-31 15:30:37 -08:00
const handleSessionData = async ( sessionDataStr : string ) = > {
try {
const sessionData = this . parseSessionData ( sessionDataStr ) ;
2024-03-15 12:13:32 -07:00
resolve ( sessionData ) ;
} catch ( err ) {
reject ( err ) ;
return ;
}
} ;
if ( ! bypassLogin ) {
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SESSION } &slot= ${ slotId } ` )
. then ( response = > response . text ( ) )
. then ( async response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . error ( response ) ;
return resolve ( null ) ;
}
2023-04-28 12:03:42 -07:00
2024-03-15 12:13:32 -07:00
await handleSessionData ( response ) ;
} ) ;
2024-03-15 12:56:07 -07:00
} else {
const sessionData = localStorage . getItem ( ` sessionData ${ slotId ? slotId : '' } ` ) ;
if ( sessionData )
await handleSessionData ( atob ( sessionData ) ) ;
else
return resolve ( null ) ;
}
2024-03-15 12:13:32 -07:00
} ) ;
}
2024-03-21 10:12:05 -07:00
loadSession ( scene : BattleScene , slotId : integer , sessionData? : SessionSaveData ) : Promise < boolean > {
2024-03-15 12:13:32 -07:00
return new Promise ( async ( resolve , reject ) = > {
try {
2024-03-21 10:12:05 -07:00
const initSessionFromData = async sessionData = > {
2023-12-31 15:30:37 -08:00
console . debug ( sessionData ) ;
2023-04-28 12:03:42 -07:00
2024-03-25 08:00:42 -07:00
scene . gameMode = gameModes [ sessionData . gameMode || GameModes . CLASSIC ] ;
2024-03-15 18:59:34 -07:00
scene . setSeed ( sessionData . seed || scene . game . config . seed [ 0 ] ) ;
2023-12-31 15:30:37 -08:00
scene . resetSeed ( ) ;
2023-04-28 12:03:42 -07:00
2024-03-29 17:11:52 -07:00
console . log ( 'Seed:' , scene . seed ) ;
2024-01-11 17:27:50 -08:00
scene . sessionPlayTime = sessionData . playTime || 0 ;
2023-12-31 15:30:37 -08:00
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
2023-05-31 16:54:57 -07:00
2023-12-31 15:30:37 -08:00
const party = scene . getParty ( ) ;
party . splice ( 0 , party . length ) ;
2023-04-28 12:03:42 -07:00
2023-12-31 15:30:37 -08: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 12:03:42 -07:00
2023-12-31 15:30:37 -08:00
Object . keys ( scene . pokeballCounts ) . forEach ( ( key : string ) = > {
scene . pokeballCounts [ key ] = sessionData . pokeballCounts [ key ] || 0 ;
} ) ;
2023-04-28 12:03:42 -07:00
2023-12-31 15:30:37 -08:00
scene . money = sessionData . money || 0 ;
scene . updateMoneyText ( ) ;
2023-04-28 12:03:42 -07:00
2024-01-11 09:26:32 -08:00
if ( scene . money > this . gameStats . highestMoney )
this . gameStats . highestMoney = scene . money ;
2024-03-17 08:36:19 -07:00
scene . score = sessionData . score ;
2024-03-18 21:03:41 -07:00
scene . updateScoreText ( ) ;
2024-03-17 08:36:19 -07:00
2023-12-31 15:30:37 -08:00
const battleType = sessionData . battleType || 0 ;
2024-03-20 22:05:46 -07: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 06:22:51 -08:00
battle . enemyLevels = sessionData . enemyParty . map ( p = > p . level ) ;
2023-11-10 12:51:34 -08:00
2023-12-31 15:30:37 -08:00
scene . newArena ( sessionData . arena . biome , true ) ;
2023-10-07 13:08:33 -07:00
2023-12-31 15:30:37 -08:00
sessionData . enemyParty . forEach ( ( enemyData , e ) = > {
2024-03-21 09:34:19 -07:00
const enemyPokemon = enemyData . toPokemon ( scene , battleType , e , sessionData . trainer ? . variant === TrainerVariant . DOUBLE ) as EnemyPokemon ;
2023-12-31 15:30:37 -08:00
battle . enemyParty [ e ] = enemyPokemon ;
if ( battleType === BattleType . WILD )
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2023-10-18 15:01:15 -07:00
2023-12-31 15:30:37 -08:00
loadPokemonAssets . push ( enemyPokemon . loadAssets ( ) ) ;
} ) ;
2023-05-18 08:11:06 -07:00
2023-12-31 15:30:37 -08:00
scene . arena . weather = sessionData . arena . weather ;
// TODO
//scene.arena.tags = sessionData.arena.tags;
2023-04-28 12:03:42 -07:00
2023-12-31 15:30:37 -08:00
const modifiersModule = await import ( '../modifier/modifier' ) ;
2023-04-28 12:03:42 -07:00
2023-12-31 15:30:37 -08:00
for ( let modifierData of sessionData . modifiers ) {
const modifier = modifierData . toModifier ( scene , modifiersModule [ modifierData . className ] ) ;
if ( modifier )
scene . addModifier ( modifier , true ) ;
}
2023-04-28 12:03:42 -07:00
2023-12-31 15:30:37 -08:00
scene . updateModifiers ( true ) ;
2023-04-28 12:03:42 -07:00
2024-03-15 12:13:32 -07:00
for ( let enemyModifierData of sessionData . enemyModifiers ) {
const modifier = enemyModifierData . toModifier ( scene , modifiersModule [ enemyModifierData . className ] ) ;
if ( modifier )
scene . addEnemyModifier ( modifier , true ) ;
2024-02-17 08:18:53 -08:00
}
2023-04-28 12:03:42 -07:00
2024-03-15 12:13:32 -07:00
scene . updateModifiers ( false ) ;
2023-12-31 15:30:37 -08:00
Promise . all ( loadPokemonAssets ) . then ( ( ) = > resolve ( true ) ) ;
2024-03-21 10:12:05 -07:00
} ;
if ( sessionData )
initSessionFromData ( sessionData ) ;
else {
this . getSession ( slotId )
. then ( data = > initSessionFromData ( data ) )
. catch ( err = > {
reject ( err ) ;
return ;
} ) ;
}
2024-03-15 12:13:32 -07:00
} catch ( err ) {
reject ( err ) ;
return ;
}
2023-04-28 12:03:42 -07:00
} ) ;
}
2024-03-16 19:06:56 -07:00
deleteSession ( slotId : integer ) : Promise < boolean > {
2023-12-31 19:49:50 -08: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-03-15 12:13:32 -07:00
Utils . apiFetch ( ` savedata/delete?datatype= ${ GameDataType . SESSION } &slot= ${ slotId } ` ) . then ( response = > {
2023-12-31 19:49:50 -08:00
if ( response . ok ) {
2024-03-15 12:13:32 -07:00
loggedInUser . lastSessionSlot = - 1 ;
2023-12-31 19:49:50 -08:00
return resolve ( true ) ;
}
resolve ( false ) ;
} ) ;
} ) ;
} ) ;
2023-04-28 12:03:42 -07:00
}
2024-03-17 08:36:19 -07:00
tryClearSession ( scene : BattleScene , slotId : integer ) : Promise < [ success : boolean , newClear : boolean ] > {
2024-03-16 19:06:56 -07: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 08:36:19 -07:00
const sessionData = this . getSessionSaveData ( scene ) ;
Utils . apiPost ( ` savedata/clear?slot= ${ slotId } ` , JSON . stringify ( sessionData ) ) . then ( response = > {
2024-03-16 19:06:56 -07:00
if ( response . ok ) {
loggedInUser . lastSessionSlot = - 1 ;
return response . json ( ) ;
}
resolve ( [ false , false ] ) ;
} ) . then ( jsonResponse = > resolve ( [ true , jsonResponse . success as boolean ] ) ) ;
} ) ;
} ) ;
}
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 ) ) ) ;
} * /
2024-01-12 17:16:29 -08:00
if ( k === 'party' || k === 'enemyParty' ) {
2023-12-26 11:49:23 -08:00
const ret : PokemonData [ ] = [ ] ;
2023-12-31 15:30:37 -08:00
if ( v === null )
v = [ ] ;
2023-12-26 11:49:23 -08: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 15:30:37 -08:00
if ( v === null )
v = [ ] ;
2023-12-26 11:49:23 -08: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 12:13:32 -07: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 ) {
Utils . apiFetch ( ` savedata/get?datatype= ${ dataType } ${ dataType === GameDataType . SESSION ? ` &slot= ${ slotId } ` : '' } ` )
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . error ( response ) ;
resolve ( false ) ;
return ;
}
2023-12-31 15:30:37 -08:00
2024-03-15 12:13:32 -07:00
handleData ( response ) ;
resolve ( true ) ;
} ) ;
} else {
const data = localStorage . getItem ( dataKey ) ;
if ( data )
handleData ( atob ( data ) ) ;
resolve ( ! ! data ) ;
}
} ) ;
2023-12-26 11:49:23 -08:00
}
2024-03-15 12:13:32 -07:00
public importData ( dataType : GameDataType , slotId : integer = 0 ) : void {
const dataKey = getDataTypeKey ( dataType , slotId ) ;
2023-12-26 11:49:23 -08: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 10:39:04 -08:00
let dataStr = AES . decrypt ( e . target . result . toString ( ) , saveKey ) . toString ( enc . Utf8 ) ;
2023-12-26 11:49:23 -08:00
let valid = false ;
try {
switch ( dataType ) {
case GameDataType . SYSTEM :
2023-12-31 10:39:04 -08:00
dataStr = this . convertSystemDataStr ( dataStr ) ;
2023-12-26 11:49:23 -08: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 15:42:11 -08:00
case GameDataType . TUTORIALS :
2023-12-26 11:49:23 -08: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 15:42:11 -08:00
case GameDataType . TUTORIALS :
dataName = 'tutorials' ;
break ;
2023-12-26 11:49:23 -08:00
}
2023-12-31 15:30:37 -08:00
const displayError = ( error : string ) = > this . scene . ui . showText ( error , null , ( ) = > this . scene . ui . showText ( null , 0 ) , Utils . fixedInt ( 1500 ) ) ;
2023-12-26 11:49:23 -08: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 15:42:11 -08:00
if ( ! bypassLogin && dataType < GameDataType . SETTINGS ) {
2023-12-31 15:30:37 -08:00
updateUserInfo ( ) . then ( success = > {
if ( ! success )
return displayError ( ` Could not contact the server. Your ${ dataName } data could not be imported. ` ) ;
2024-03-15 12:13:32 -07:00
Utils . apiPost ( ` savedata/update?datatype= ${ dataType } ${ dataType === GameDataType . SESSION ? ` &slot= ${ slotId } ` : '' } ` , dataStr )
2023-12-31 15:30:37 -08: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 11:49:23 -08:00
} , ( ) = > {
this . scene . ui . revertMode ( ) ;
this . scene . ui . showText ( null , 0 ) ;
2024-02-06 20:11:00 -08:00
} , false , - 98 ) ;
2023-12-26 11:49:23 -08: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 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 ] = {
2024-01-05 19:24:05 -08:00
seenAttr : 0n , caughtAttr : 0n , natureAttr : 0 , seenCount : 0 , caughtCount : 0 , hatchedCount : 0 , ivs : [ 0 , 0 , 0 , 0 , 0 , 0 ]
2023-11-12 20:47:04 -08:00
} ;
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 ;
2024-01-05 19:24:05 -08:00
const defaultStarterNatures : Nature [ ] = [ ] ;
this . scene . executeWithSeedOffset ( ( ) = > {
const neutralNatures = [ Nature . HARDY , Nature . DOCILE , Nature . SERIOUS , Nature . BASHFUL , Nature . QUIRKY ] ;
for ( let s = 0 ; s < defaultStarters . length ; s ++ )
2024-02-16 21:40:03 -08:00
defaultStarterNatures . push ( Utils . randSeedItem ( neutralNatures ) ) ;
2024-01-05 19:24:05 -08:00
} , 0 , 'default' ) ;
for ( let ds = 0 ; ds < defaultStarters . length ; ds ++ ) {
let entry = data [ defaultStarters [ ds ] ] as DexEntry ;
2023-11-12 20:47:04 -08:00
entry . seenAttr = defaultStarterAttr ;
entry . caughtAttr = defaultStarterAttr ;
2024-01-05 19:24:05 -08:00
entry . natureAttr = Math . pow ( 2 , defaultStarterNatures [ ds ] + 1 ) ;
2023-11-12 20:47:04 -08:00
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
2024-01-05 19:24:05 -08:00
this . defaultDexData = Object . assign ( { } , data ) ;
2023-04-17 22:32:26 -07:00
this . dexData = data ;
}
2024-02-25 09:45:41 -08:00
private initEggMoveData ( ) : void {
const data : StarterEggMoveData = { } ;
const starterSpeciesIds = Object . keys ( speciesEggMoves ) . map ( k = > parseInt ( k ) as Species ) ;
for ( let speciesId of starterSpeciesIds )
data [ speciesId ] = 0 ;
2024-03-01 16:37:32 -08:00
this . starterEggMoveData = data ;
2024-02-25 09:45:41 -08:00
}
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 ( ) ;
2024-01-11 09:26:32 -08:00
if ( incrementCount ) {
2023-11-12 20:47:04 -08:00
dexEntry . seenCount ++ ;
2024-01-11 09:26:32 -08:00
this . gameStats . pokemonSeen ++ ;
if ( pokemon . isShiny ( ) )
this . gameStats . shinyPokemonSeen ++ ;
}
2023-04-17 22:32:26 -07:00
}
2023-04-17 19:44:41 -07:00
2023-12-31 10:20:28 -08:00
setPokemonCaught ( pokemon : Pokemon , incrementCount : boolean = true , fromEgg : boolean = false ) : Promise < void > {
return this . setPokemonSpeciesCaught ( pokemon , pokemon . species , incrementCount , fromEgg ) ;
2023-07-05 11:19:49 -07:00
}
2023-12-31 10:20:28 -08:00
setPokemonSpeciesCaught ( pokemon : Pokemon , species : PokemonSpecies , incrementCount : boolean = true , fromEgg : boolean = false ) : Promise < void > {
2024-02-25 09:45:41 -08: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 ( ) ;
2024-01-05 19:24:05 -08:00
dexEntry . natureAttr |= Math . pow ( 2 , pokemon . nature + 1 ) ;
2023-12-31 10:20:28 -08:00
if ( incrementCount ) {
2024-01-11 09:26:32 -08:00
if ( ! fromEgg ) {
2023-12-31 10:20:28 -08:00
dexEntry . caughtCount ++ ;
2024-01-11 09:26:32 -08: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 10:20:28 -08:00
dexEntry . hatchedCount ++ ;
2024-01-11 09:26:32 -08: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-12-31 10:20:28 -08:00
}
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 ] ;
2023-12-31 10:20:28 -08:00
return this . setPokemonSpeciesCaught ( pokemon , getPokemonSpecies ( prevolutionSpecies ) , incrementCount , fromEgg ) . then ( ( ) = > resolve ( ) ) ;
2023-12-01 14:23:26 -08:00
} 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
} ) ;
}
2024-02-25 09:45:41 -08: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 ;
}
if ( ! this . starterEggMoveData . hasOwnProperty ( speciesId ) )
this . starterEggMoveData [ speciesId ] = 0 ;
const value = Math . pow ( 2 , eggMoveIndex ) ;
2024-03-01 19:18:39 -08:00
if ( this . starterEggMoveData [ speciesId ] & value ) {
2024-02-25 09:45:41 -08:00
resolve ( false ) ;
return ;
}
this . starterEggMoveData [ speciesId ] |= value ;
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' , 1500 ) ;
this . scene . ui . showText ( ` ${ eggMoveIndex === 3 ? 'Rare ' : '' } Egg Move unlocked: ${ allMoves [ speciesEggMoves [ speciesId ] [ eggMoveIndex ] ] . name } ` , null , ( ) = > resolve ( true ) , null , true ) ;
} ) ;
}
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 ] ) ) ;
}
2024-02-18 19:21:57 -08:00
getSpeciesDefaultDexAttr ( species : PokemonSpecies , forSeen : boolean = false ) : bigint {
2023-11-12 20:47:04 -08:00
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
2024-01-05 19:24:05 -08: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 13:38:46 -08:00
getSpeciesStarterValue ( speciesId : Species ) : number {
const baseValue = speciesStarters [ speciesId ] ;
let value = baseValue ;
const caughtHatchedCount = this . dexData [ speciesId ] . caughtCount + this . dexData [ speciesId ] . hatchedCount ;
const decrementValue = ( value : number ) = > {
if ( value > 1 )
value -- ;
else
value /= 2 ;
return value ;
}
let thresholdA : integer ;
let thresholdB : integer ;
2024-03-28 14:29:31 -07:00
switch ( baseValue ) {
case 1 :
[ thresholdA , thresholdB ] = [ 25 , 100 ] ;
break ;
case 2 :
[ thresholdA , thresholdB ] = [ 20 , 70 ] ;
break ;
case 3 :
[ thresholdA , thresholdB ] = [ 15 , 50 ] ;
break ;
case 4 :
[ thresholdA , thresholdB ] = [ 10 , 30 ] ;
break ;
case 5 :
[ thresholdA , thresholdB ] = [ 8 , 25 ] ;
break ;
case 6 :
[ thresholdA , thresholdB ] = [ 5 , 15 ] ;
break ;
case 7 :
[ thresholdA , thresholdB ] = [ 4 , 12 ] ;
break ;
case 8 :
[ thresholdA , thresholdB ] = [ 3 , 10 ] ;
break ;
default :
[ thresholdA , thresholdB ] = [ 2 , 5 ] ;
break ;
}
2024-02-12 13:38:46 -08:00
if ( caughtHatchedCount >= thresholdA ) {
value = decrementValue ( value ) ;
if ( caughtHatchedCount >= thresholdB )
value = decrementValue ( value ) ;
}
return value ;
}
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-12-31 10:20:28 -08: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 19:24:05 -08: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 09:50:21 -07:00
}
}
2023-04-17 19:44:41 -07:00
}