2024-06-24 09:14:18 +09:00
import i18next from "i18next" ;
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" ;
2023-04-20 15:46:05 -04:00
import * as Utils from "../utils" ;
2024-05-23 17:03:10 +02:00
import * as Overrides from "../overrides" ;
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-06-08 15:07:23 +10:00
import { GameModes , getGameMode } 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" ;
2024-06-03 19:57:47 -04:00
import { SettingKeys , resetSettings , setSetting } from "./settings/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" ;
2024-05-15 00:52:06 -04:00
import { clientSessionId , 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-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" ;
2024-06-03 19:57:47 -04:00
import { setSettingGamepad , SettingGamepad , settingGamepadDefaults } from "./settings/settings-gamepad" ;
import { setSettingKeyboard , SettingKeyboard } from "#app/system/settings/settings-keyboard" ;
2024-06-13 18:44:23 -04:00
import { TerrainChangedEvent , WeatherChangedEvent } from "#app/events/arena.js" ;
2024-06-04 17:39:22 -04:00
import { EnemyAttackStatusEffectChanceModifier } from "../modifier/modifier" ;
import { StatusEffect } from "#app/data/status-effect.js" ;
2024-06-08 15:07:23 +10:00
import ChallengeData from "./challenge-data" ;
2024-06-13 18:44:23 -04:00
import { Device } from "#enums/devices" ;
import { GameDataType } from "#enums/game-data-type" ;
import { Moves } from "#enums/moves" ;
import { PlayerGender } from "#enums/player-gender" ;
import { Species } from "#enums/species" ;
export const defaultStarterSpecies : 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 ,
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-12-26 14:49:23 -05:00
2024-05-23 17:03:10 +02:00
const saveKey = "x0i2O7WRiANTqPmZ" ; // Temporary; secure encryption is not yet necessary
2023-12-26 14:49:23 -05:00
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 ) {
2024-05-23 17:03:10 +02:00
case GameDataType . SYSTEM :
return "data" ;
case GameDataType . SESSION :
let ret = "sessionData" ;
if ( slotId ) {
ret += slotId ;
}
return ret ;
case GameDataType . SETTINGS :
return "settings" ;
case GameDataType . TUTORIALS :
return "tutorials" ;
2024-06-03 12:49:13 +02:00
case GameDataType . SEEN_DIALOGUES :
return "seenDialogues" ;
2023-12-26 14:49:23 -05:00
}
}
2023-04-17 22:44:41 -04:00
2024-06-08 00:33:45 +02:00
export function encrypt ( data : string , bypassLogin : boolean ) : string {
2024-05-15 00:52:06 -04:00
return ( bypassLogin
? ( data : string ) = > btoa ( data )
: ( data : string ) = > AES . encrypt ( data , saveKey ) ) ( data ) ;
}
2024-06-08 00:33:45 +02:00
export function decrypt ( data : string , bypassLogin : boolean ) : string {
2024-05-15 00:52:06 -04:00
return ( bypassLogin
? ( data : string ) = > atob ( data )
: ( data : string ) = > AES . decrypt ( data , saveKey ) . toString ( enc . Utf8 ) ) ( data ) ;
}
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-06-04 05:43:52 +10:00
eggPity : integer [ ] ;
unlockPity : integer [ ] ;
2023-04-28 15:03:42 -04:00
}
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 ;
2024-06-08 15:07:23 +10:00
challenges : ChallengeData [ ] ;
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 {
2024-06-17 06:49:29 +02:00
[ type : string ] : integer ;
2023-12-19 23:51:48 -05:00
}
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
2024-05-23 17:03:10 +02:00
} ;
2023-11-12 23:47:04 -05:00
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-05-23 17:03:10 +02:00
} ;
2024-04-18 22:52:26 -04:00
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-06-17 06:49:29 +02:00
export interface StarterAttributes {
nature? : integer ;
ability? : integer ;
variant? : integer ;
form? : integer ;
female? : boolean ;
}
export interface StarterPreferences {
[ key : integer ] : StarterAttributes ;
}
// the latest data saved/loaded for the Starter Preferences. Required to reduce read/writes. Initialize as "{}", since this is the default value and no data needs to be stored if present.
// if they ever add private static variables, move this into StarterPrefs
const StarterPrefers_DEFAULT : string = "{}" ;
let StarterPrefers_private_latest : string = StarterPrefers_DEFAULT ;
// This is its own class as StarterPreferences...
// - don't need to be loaded on startup
// - isn't stored with other data
// - don't require to be encrypted
// - shouldn't require calls outside of the starter selection
export class StarterPrefs {
// called on starter selection show once
static load ( ) : StarterPreferences {
return JSON . parse (
StarterPrefers_private_latest = ( localStorage . getItem ( ` starterPrefs_ ${ loggedInUser ? . username } ` ) || StarterPrefers_DEFAULT )
) ;
}
// called on starter selection clear, always
static save ( prefs : StarterPreferences ) : void {
const pStr : string = JSON . stringify ( prefs ) ;
if ( pStr !== StarterPrefers_private_latest ) {
// something changed, store the update
localStorage . setItem ( ` starterPrefs_ ${ loggedInUser ? . username } ` , pStr ) ;
}
}
}
2024-04-13 18:59:58 -04:00
export interface StarterDataEntry {
2024-05-24 01:45:04 +02:00
moveset : StarterMoveset | StarterFormMoveData ;
2024-04-13 18:59:58 -04:00
eggMoves : integer ;
candyCount : integer ;
2024-05-02 17:48:08 -04:00
friendship : integer ;
2024-04-18 22:52:26 -04:00
abilityAttr : integer ;
2024-04-13 18:59:58 -04:00
passiveAttr : integer ;
valueReduction : integer ;
2024-05-07 00:44:41 -04:00
classicWinCount : 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
}
2024-06-03 12:49:13 +02:00
export interface SeenDialogues {
[ key : string ] : boolean ;
}
2023-12-31 13:39:04 -05:00
const systemShortKeys = {
2024-05-23 17:03:10 +02:00
seenAttr : "$sa" ,
caughtAttr : "$ca" ,
natureAttr : "$na" ,
seenCount : "$s" ,
caughtCount : "$c" ,
hatchedCount : "$hc" ,
ivs : "$i" ,
moveset : "$m" ,
eggMoves : "$em" ,
candyCount : "$x" ,
friendship : "$f" ,
abilityAttr : "$a" ,
passiveAttr : "$pa" ,
valueReduction : "$vr" ,
classicWinCount : "$wc"
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 ;
2024-05-24 01:45:04 +02:00
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 [ ] ;
2024-06-04 05:43:52 +10:00
public eggPity : integer [ ] ;
public unlockPity : integer [ ] ;
2023-12-15 23:07:32 -05:00
2023-04-17 22:44:41 -04:00
constructor ( scene : BattleScene ) {
this . scene = scene ;
2023-10-26 16:33:59 -04:00
this . loadSettings ( ) ;
2024-06-01 14:56:32 +02:00
this . loadGamepadSettings ( ) ;
this . loadMappingConfigs ( ) ;
2024-04-25 09:17:48 -04:00
this . trainerId = Utils . randInt ( 65536 ) ;
this . secretId = Utils . randInt ( 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 = [ ] ;
2024-06-04 05:43:52 +10:00
this . eggPity = [ 0 , 0 , 0 , 0 ] ;
this . unlockPity = [ 0 , 0 , 0 , 0 ] ;
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
}
2024-05-12 23:01:05 -04:00
public getSystemSaveData ( ) : SystemSaveData {
return {
trainerId : this.trainerId ,
secretId : this.secretId ,
gender : this.gender ,
dexData : this.dexData ,
starterData : this.starterData ,
gameStats : this.gameStats ,
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 ,
2024-06-04 05:43:52 +10:00
timestamp : new Date ( ) . getTime ( ) ,
eggPity : this.eggPity.slice ( 0 ) ,
unlockPity : this.unlockPity.slice ( 0 )
2024-05-12 23:01:05 -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-05-12 23:01:05 -04:00
const data = this . getSystemSaveData ( ) ;
2024-05-07 13:36:52 -04:00
const maxIntAttrValue = Math . pow ( 2 , 31 ) ;
2024-05-23 17:03:10 +02:00
const systemData = JSON . stringify ( data , ( k : any , v : any ) = > typeof v === "bigint" ? v <= maxIntAttrValue ? Number ( v ) : v . toString ( ) : v ) ;
2024-05-07 13:36:52 -04:00
2024-05-15 00:52:06 -04:00
localStorage . setItem ( ` data_ ${ loggedInUser . username } ` , encrypt ( systemData , bypassLogin ) ) ;
2024-05-13 20:56:28 -04:00
if ( ! bypassLogin ) {
2024-06-16 02:24:50 +01:00
Utils . apiPost ( ` savedata/system/update?clientSessionId= ${ clientSessionId } ` , systemData , undefined , true )
2024-05-07 13:36:52 -04:00
. then ( response = > response . text ( ) )
. then ( error = > {
this . scene . ui . savingIcon . hide ( ) ;
if ( error ) {
2024-05-23 17:03:10 +02:00
if ( error . startsWith ( "client version out of date" ) ) {
2024-05-07 13:36:52 -04:00
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new OutdatedPhase ( this . scene ) ) ;
2024-05-23 17:03:10 +02:00
} else if ( error . startsWith ( "session out of date" ) ) {
2024-05-07 13:36:52 -04:00
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
2023-12-31 18:30:37 -05:00
}
2024-05-07 13:36:52 -04:00
console . error ( error ) ;
return resolve ( false ) ;
}
resolve ( true ) ;
} ) ;
} else {
this . scene . ui . savingIcon . hide ( ) ;
2024-04-04 10:16:29 -04:00
2024-05-07 13:36:52 -04:00
resolve ( true ) ;
}
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 = > {
2024-05-23 17:03:10 +02:00
console . log ( "Client Session:" , clientSessionId ) ;
2024-05-15 00:52:06 -04:00
2024-05-23 17:03:10 +02:00
if ( bypassLogin && ! localStorage . getItem ( ` data_ ${ loggedInUser . username } ` ) ) {
2024-02-19 20:36:10 -05:00
return resolve ( false ) ;
2024-05-23 17:03:10 +02:00
}
2023-04-18 01:32:26 -04:00
2024-05-15 00:52:06 -04:00
if ( ! bypassLogin ) {
2024-06-16 02:24:50 +01:00
Utils . apiFetch ( ` savedata/system/get?clientSessionId= ${ clientSessionId } ` , true )
2024-05-15 00:52:06 -04:00
. then ( response = > response . text ( ) )
. then ( response = > {
2024-05-23 17:03:10 +02:00
if ( ! response . length || response [ 0 ] !== "{" ) {
if ( response . startsWith ( "sql: no rows in result set" ) ) {
this . scene . queueMessage ( "Save data could not be found. If this is a new account, you can safely ignore this message." , null , true ) ;
2024-05-15 00:52:06 -04:00
return resolve ( true ) ;
2024-05-23 17:03:10 +02:00
} else if ( response . indexOf ( "Too many connections" ) > - 1 ) {
this . scene . queueMessage ( "Too many people are trying to connect and the server is overloaded. Please try again later." , null , true ) ;
2024-05-15 00:52:06 -04:00
return resolve ( false ) ;
}
console . error ( response ) ;
return resolve ( false ) ;
}
2023-11-12 23:47:04 -05:00
2024-05-15 00:52:06 -04:00
const cachedSystem = localStorage . getItem ( ` data_ ${ loggedInUser . username } ` ) ;
this . initSystem ( response , cachedSystem ? AES . decrypt ( cachedSystem , saveKey ) . toString ( enc . Utf8 ) : null ) . then ( resolve ) ;
} ) ;
2024-05-23 17:03:10 +02:00
} else {
2024-05-15 00:52:06 -04:00
this . initSystem ( decrypt ( localStorage . getItem ( ` data_ ${ loggedInUser . username } ` ) , bypassLogin ) ) . then ( resolve ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
} ) ;
}
2023-04-18 01:32:26 -04:00
2024-05-15 00:52:06 -04:00
public initSystem ( systemDataStr : string , cachedSystemDataStr? : string ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
try {
let systemData = this . parseSystemData ( systemDataStr ) ;
if ( cachedSystemDataStr ) {
2024-05-23 17:03:10 +02:00
const cachedSystemData = this . parseSystemData ( cachedSystemDataStr ) ;
2024-05-15 00:52:06 -04:00
if ( cachedSystemData . timestamp > systemData . timestamp ) {
2024-05-23 17:03:10 +02:00
console . debug ( "Use cached system" ) ;
2024-05-15 00:52:06 -04:00
systemData = cachedSystemData ;
2024-05-15 11:42:18 -04:00
systemDataStr = cachedSystemDataStr ;
2024-05-23 17:03:10 +02:00
} else {
2024-05-15 00:52:06 -04:00
this . clearLocalData ( ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
}
2023-12-14 00:41:35 -05:00
2024-05-15 00:52:06 -04:00
console . debug ( systemData ) ;
2023-04-17 22:44:41 -04:00
2024-05-15 11:42:18 -04:00
localStorage . setItem ( ` data_ ${ loggedInUser . username } ` , encrypt ( systemDataStr , bypassLogin ) ) ;
2024-05-15 00:52:06 -04: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 ' ] ;
2024-05-24 01:45:04 +02:00
2024-05-15 00:52:06 -04:00
if ( versions [ 0 ] !== versions [ 1 ] ) {
const [ versionNumbers , oldVersionNumbers ] = versions . map ( ver = > ver . split ( '.' ) . map ( v = > parseInt ( v ) ) ) ;
} * /
2024-02-06 16:15:35 -05:00
2024-05-15 00:52:06 -04:00
this . trainerId = systemData . trainerId ;
this . secretId = systemData . secretId ;
2024-02-06 16:15:35 -05:00
2024-05-15 00:52:06 -04:00
this . gender = systemData . gender ;
2024-04-13 18:59:58 -04:00
2024-06-03 19:57:47 -04:00
this . saveSetting ( SettingKeys . Player_Gender , systemData . gender === PlayerGender . FEMALE ? 1 : 0 ) ;
2024-03-01 12:41:47 -05:00
2024-05-15 00:52:06 -04:00
const initStarterData = ! systemData . starterData ;
2024-04-13 18:59:58 -04:00
2024-05-15 00:52:06 -04:00
if ( initStarterData ) {
this . initStarterData ( ) ;
2024-04-20 22:30:28 -04:00
2024-05-23 17:03:10 +02:00
if ( systemData [ "starterMoveData" ] ) {
const starterMoveData = systemData [ "starterMoveData" ] ;
for ( const s of Object . keys ( starterMoveData ) ) {
2024-05-15 00:52:06 -04:00
this . starterData [ s ] . moveset = starterMoveData [ s ] ;
2024-05-23 17:03:10 +02:00
}
2024-04-18 22:52:26 -04:00
}
2024-02-21 01:03:34 -05:00
2024-05-23 17:03:10 +02:00
if ( systemData [ "starterEggMoveData" ] ) {
const starterEggMoveData = systemData [ "starterEggMoveData" ] ;
for ( const s of Object . keys ( starterEggMoveData ) ) {
2024-05-15 00:52:06 -04:00
this . starterData [ s ] . eggMoves = starterEggMoveData [ s ] ;
2024-05-23 17:03:10 +02:00
}
2024-05-05 17:11:29 -04:00
}
2024-01-11 12:26:32 -05:00
2024-05-15 00:52:06 -04:00
this . migrateStarterAbilities ( systemData , this . starterData ) ;
} else {
2024-05-23 17:03:10 +02:00
if ( [ "1.0.0" , "1.0.1" ] . includes ( systemData . gameVersion ) ) {
2024-05-15 00:52:06 -04:00
this . migrateStarterAbilities ( systemData ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
//this.fixVariantData(systemData);
this . fixStarterData ( systemData ) ;
// Migrate ability starter data if empty for caught species
Object . keys ( systemData . starterData ) . forEach ( sd = > {
2024-05-23 17:03:10 +02:00
if ( systemData . dexData [ sd ] . caughtAttr && ! systemData . starterData [ sd ] . abilityAttr ) {
2024-05-15 00:52:06 -04:00
systemData . starterData [ sd ] . abilityAttr = 1 ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
} ) ;
this . starterData = systemData . starterData ;
}
2023-12-19 23:51:48 -05:00
2024-05-15 00:52:06 -04:00
if ( systemData . gameStats ) {
2024-05-23 17:03:10 +02:00
if ( systemData . gameStats . legendaryPokemonCaught !== undefined && systemData . gameStats . subLegendaryPokemonCaught === undefined ) {
2024-05-15 00:52:06 -04:00
this . fixLegendaryStats ( systemData ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
this . gameStats = systemData . gameStats ;
}
2023-12-31 18:30:37 -05:00
2024-05-15 00:52:06 -04:00
if ( systemData . unlocks ) {
2024-05-23 17:03:10 +02:00
for ( const key of Object . keys ( systemData . unlocks ) ) {
if ( this . unlocks . hasOwnProperty ( key ) ) {
2024-05-15 00:52:06 -04:00
this . unlocks [ key ] = systemData . unlocks [ key ] ;
2024-05-23 17:03:10 +02:00
}
2023-12-31 18:30:37 -05:00
}
2024-05-15 00:52:06 -04:00
}
2023-12-19 23:51:48 -05:00
2024-05-15 00:52:06 -04:00
if ( systemData . achvUnlocks ) {
2024-05-23 17:03:10 +02:00
for ( const a of Object . keys ( systemData . achvUnlocks ) ) {
if ( achvs . hasOwnProperty ( a ) ) {
2024-05-15 00:52:06 -04:00
this . achvUnlocks [ a ] = systemData . achvUnlocks [ a ] ;
2024-05-23 17:03:10 +02:00
}
2024-05-24 01:45:04 +02:00
}
2024-05-15 00:52:06 -04:00
}
2023-12-15 23:07:32 -05:00
2024-05-15 00:52:06 -04:00
if ( systemData . voucherUnlocks ) {
2024-05-23 17:03:10 +02:00
for ( const v of Object . keys ( systemData . voucherUnlocks ) ) {
if ( vouchers . hasOwnProperty ( v ) ) {
2024-05-15 00:52:06 -04:00
this . voucherUnlocks [ v ] = systemData . voucherUnlocks [ v ] ;
2024-05-23 17:03:10 +02:00
}
2024-04-13 18:59:58 -04:00
}
2024-05-15 00:52:06 -04:00
}
2024-04-13 18:59:58 -04:00
2024-05-15 00:52:06 -04:00
if ( systemData . voucherCounts ) {
Utils . getEnumKeys ( VoucherType ) . forEach ( key = > {
const index = VoucherType [ key ] ;
this . voucherCounts [ index ] = systemData . voucherCounts [ index ] || 0 ;
} ) ;
2024-02-19 20:36:10 -05:00
}
2023-12-31 18:30:37 -05:00
2024-05-15 00:52:06 -04:00
this . eggs = systemData . eggs
? systemData . eggs . map ( e = > e . toEgg ( ) )
: [ ] ;
2024-06-04 05:43:52 +10:00
this . eggPity = systemData . eggPity ? systemData . eggPity . slice ( 0 ) : [ 0 , 0 , 0 , 0 ] ;
this . unlockPity = systemData . unlockPity ? systemData . unlockPity . slice ( 0 ) : [ 0 , 0 , 0 , 0 ] ;
2024-05-15 00:52:06 -04:00
this . dexData = Object . assign ( this . dexData , systemData . dexData ) ;
this . consolidateDexData ( this . dexData ) ;
this . defaultDexData = null ;
if ( initStarterData ) {
const starterIds = Object . keys ( this . starterData ) . map ( s = > parseInt ( s ) as Species ) ;
2024-05-23 17:03:10 +02:00
for ( const s of starterIds ) {
2024-05-15 00:52:06 -04:00
this . starterData [ s ] . candyCount += this . dexData [ s ] . caughtCount ;
this . starterData [ s ] . candyCount += this . dexData [ s ] . hatchedCount * 2 ;
2024-05-23 17:03:10 +02:00
if ( this . dexData [ s ] . caughtAttr & DexAttr . SHINY ) {
2024-05-15 00:52:06 -04:00
this . starterData [ s ] . candyCount += 4 ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
}
}
2023-12-31 18:30:37 -05:00
2024-05-15 00:52:06 -04:00
resolve ( true ) ;
} catch ( err ) {
console . error ( err ) ;
resolve ( false ) ;
}
2023-12-31 18:30:37 -05:00
} ) ;
2023-04-17 22:44:41 -04:00
}
2024-06-08 00:33:45 +02:00
parseSystemData ( dataStr : string ) : SystemSaveData {
2023-12-26 14:49:23 -05:00
return JSON . parse ( dataStr , ( k : string , v : any ) = > {
2024-05-23 17:03:10 +02:00
if ( k === "gameStats" ) {
2024-01-11 12:26:32 -05:00
return new GameStats ( v ) ;
2024-05-23 17:03:10 +02:00
} else if ( k === "eggs" ) {
2023-12-26 14:49:23 -05:00
const ret : EggData [ ] = [ ] ;
2024-05-23 17:03:10 +02:00
if ( v === null ) {
2023-12-31 18:53:16 -05:00
v = [ ] ;
2024-05-23 17:03:10 +02:00
}
for ( const e of v ) {
2023-12-26 14:49:23 -05:00
ret . push ( new EggData ( e ) ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-26 14:49:23 -05:00
return ret ;
}
2024-05-23 17:03:10 +02:00
return k . endsWith ( "Attr" ) && ! [ "natureAttr" , "abilityAttr" , "passiveAttr" ] . includes ( k ) ? BigInt ( v ) : v ;
2023-12-26 14:49:23 -05:00
} ) as SystemSaveData ;
}
2024-06-08 00:33:45 +02:00
convertSystemDataStr ( dataStr : string , shorten : boolean = false ) : string {
2024-05-10 18:12:33 -04:00
if ( ! shorten ) {
// Account for past key oversight
2024-05-23 17:03:10 +02:00
dataStr = dataStr . replace ( /\$pAttr/g , "$pa" ) ;
2024-05-10 18:12:33 -04:00
}
2023-12-31 13:39:04 -05:00
const fromKeys = shorten ? Object . keys ( systemShortKeys ) : Object . values ( systemShortKeys ) ;
const toKeys = shorten ? Object . values ( systemShortKeys ) : Object . keys ( systemShortKeys ) ;
2024-05-23 17:03:10 +02:00
for ( const k in fromKeys ) {
dataStr = dataStr . replace ( new RegExp ( ` ${ fromKeys [ k ] . replace ( "$" , "\\$" ) } ` , "g" ) , toKeys [ k ] ) ;
}
2023-12-31 13:39:04 -05:00
return dataStr ;
}
2024-05-15 00:52:06 -04:00
public async verify ( ) : Promise < boolean > {
2024-05-23 17:03:10 +02:00
if ( bypassLogin ) {
return true ;
}
2024-05-15 00:52:06 -04:00
2024-06-16 02:24:50 +01:00
const response = await Utils . apiFetch ( ` savedata/system/verify?clientSessionId= ${ clientSessionId } ` , true )
2024-05-15 00:52:06 -04:00
. then ( response = > response . json ( ) ) ;
if ( ! response . valid ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene , JSON . stringify ( response . systemData ) ) ) ;
this . clearLocalData ( ) ;
return false ;
}
return true ;
}
public clearLocalData ( ) : void {
2024-05-23 17:03:10 +02:00
if ( bypassLogin ) {
2024-05-15 01:54:15 -04:00
return ;
2024-05-23 17:03:10 +02:00
}
2024-05-15 00:52:06 -04:00
localStorage . removeItem ( ` data_ ${ loggedInUser . username } ` ) ;
2024-05-23 17:03:10 +02:00
for ( let s = 0 ; s < 5 ; s ++ ) {
localStorage . removeItem ( ` sessionData ${ s ? s : "" } _ ${ loggedInUser . username } ` ) ;
}
2024-05-15 00:52:06 -04:00
}
2024-06-03 19:57:47 -04:00
/ * *
* Saves a setting to localStorage
* @param setting string ideally of SettingKeys
* @param valueIndex index of the setting ' s option
* @returns true
* /
public saveSetting ( setting : string , valueIndex : integer ) : boolean {
2023-10-26 16:33:59 -04:00
let settings : object = { } ;
2024-05-23 17:03:10 +02:00
if ( localStorage . hasOwnProperty ( "settings" ) ) {
settings = JSON . parse ( localStorage . getItem ( "settings" ) ) ;
}
2023-10-26 16:33:59 -04:00
2024-06-03 19:57:47 -04:00
setSetting ( this . scene , setting , valueIndex ) ;
2023-10-26 16:33:59 -04:00
2024-06-03 19:57:47 -04:00
settings [ setting ] = valueIndex ;
2023-10-26 16:33:59 -04:00
2024-05-23 17:03:10 +02:00
localStorage . setItem ( "settings" , JSON . stringify ( settings ) ) ;
2023-10-26 16:33:59 -04:00
return true ;
}
2024-06-01 14:56:32 +02:00
/ * *
* Saves the mapping configurations for a specified device .
*
* @param deviceName - The name of the device for which the configurations are being saved .
* @param config - The configuration object containing custom mapping details .
* @returns ` true ` if the configurations are successfully saved .
* /
public saveMappingConfigs ( deviceName : string , config ) : boolean {
const key = deviceName . toLowerCase ( ) ; // Convert the gamepad name to lowercase to use as a key
let mappingConfigs : object = { } ; // Initialize an empty object to hold the mapping configurations
if ( localStorage . hasOwnProperty ( "mappingConfigs" ) ) { // Check if 'mappingConfigs' exists in localStorage
mappingConfigs = JSON . parse ( localStorage . getItem ( "mappingConfigs" ) ) ;
} // Parse the existing 'mappingConfigs' from localStorage
if ( ! mappingConfigs [ key ] ) {
mappingConfigs [ key ] = { } ;
} // If there is no configuration for the given key, create an empty object for it
mappingConfigs [ key ] . custom = config . custom ; // Assign the custom configuration to the mapping configuration for the given key
localStorage . setItem ( "mappingConfigs" , JSON . stringify ( mappingConfigs ) ) ; // Save the updated mapping configurations back to localStorage
return true ; // Return true to indicate the operation was successful
}
/ * *
* Loads the mapping configurations from localStorage and injects them into the input controller .
*
* @returns ` true ` if the configurations are successfully loaded and injected ; ` false ` if no configurations are found in localStorage .
*
* @remarks
* This method checks if the 'mappingConfigs' entry exists in localStorage . If it does not exist , the method returns ` false ` .
* If 'mappingConfigs' exists , it parses the configurations and injects each configuration into the input controller
* for the corresponding gamepad or device key . The method then returns ` true ` to indicate success .
* /
public loadMappingConfigs ( ) : boolean {
if ( ! localStorage . hasOwnProperty ( "mappingConfigs" ) ) { // Check if 'mappingConfigs' exists in localStorage
return false ;
} // If 'mappingConfigs' does not exist, return false
const mappingConfigs = JSON . parse ( localStorage . getItem ( "mappingConfigs" ) ) ; // Parse the existing 'mappingConfigs' from localStorage
for ( const key of Object . keys ( mappingConfigs ) ) { // Iterate over the keys of the mapping configurations
this . scene . inputController . injectConfig ( key , mappingConfigs [ key ] ) ;
} // Inject each configuration into the input controller for the corresponding key
return true ; // Return true to indicate the operation was successful
}
public resetMappingToFactory ( ) : boolean {
if ( ! localStorage . hasOwnProperty ( "mappingConfigs" ) ) { // Check if 'mappingConfigs' exists in localStorage
return false ;
} // If 'mappingConfigs' does not exist, return false
localStorage . removeItem ( "mappingConfigs" ) ;
this . scene . inputController . resetConfigs ( ) ;
}
/ * *
* Saves a gamepad setting to localStorage .
*
* @param setting - The gamepad setting to save .
* @param valueIndex - The index of the value to set for the gamepad setting .
* @returns ` true ` if the setting is successfully saved .
*
* @remarks
* This method initializes an empty object for gamepad settings if none exist in localStorage .
* It then updates the setting in the current scene and iterates over the default gamepad settings
* to update the specified setting with the new value . Finally , it saves the updated settings back
* to localStorage and returns ` true ` to indicate success .
* /
2024-06-03 19:57:47 -04:00
public saveControlSetting ( device : Device , localStoragePropertyName : string , setting : SettingGamepad | SettingKeyboard , settingDefaults , valueIndex : integer ) : boolean {
let settingsControls : object = { } ; // Initialize an empty object to hold the gamepad settings
2024-06-01 14:56:32 +02:00
2024-06-03 19:57:47 -04:00
if ( localStorage . hasOwnProperty ( localStoragePropertyName ) ) { // Check if 'settingsControls' exists in localStorage
settingsControls = JSON . parse ( localStorage . getItem ( localStoragePropertyName ) ) ; // Parse the existing 'settingsControls' from localStorage
2024-06-01 14:56:32 +02:00
}
2024-06-03 19:57:47 -04:00
if ( device === Device . GAMEPAD ) {
setSettingGamepad ( this . scene , setting as SettingGamepad , valueIndex ) ; // Set the gamepad setting in the current scene
} else if ( device === Device . KEYBOARD ) {
setSettingKeyboard ( this . scene , setting as SettingKeyboard , valueIndex ) ; // Set the keyboard setting in the current scene
}
2024-06-01 14:56:32 +02:00
2024-06-03 19:57:47 -04:00
Object . keys ( settingDefaults ) . forEach ( s = > { // Iterate over the default gamepad settings
2024-06-01 14:56:32 +02:00
if ( s === setting ) { // If the current setting matches, update its value
2024-06-03 19:57:47 -04:00
settingsControls [ s ] = valueIndex ;
2024-06-01 14:56:32 +02:00
}
} ) ;
2024-06-03 19:57:47 -04:00
localStorage . setItem ( localStoragePropertyName , JSON . stringify ( settingsControls ) ) ; // Save the updated gamepad settings back to localStorage
2024-06-01 14:56:32 +02:00
return true ; // Return true to indicate the operation was successful
}
/ * *
2024-06-03 19:57:47 -04:00
* Loads Settings from local storage if available
* @returns true if succesful , false if not
2024-06-01 14:56:32 +02:00
* /
2023-10-26 16:33:59 -04:00
private loadSettings ( ) : boolean {
2024-06-03 19:57:47 -04:00
resetSettings ( this . scene ) ;
2023-12-25 15:03:50 -05:00
2024-05-23 17:03:10 +02:00
if ( ! localStorage . hasOwnProperty ( "settings" ) ) {
2023-10-26 16:33:59 -04:00
return false ;
2024-05-23 17:03:10 +02:00
}
2023-10-26 16:33:59 -04:00
2024-05-23 17:03:10 +02:00
const settings = JSON . parse ( localStorage . getItem ( "settings" ) ) ;
2023-10-26 16:33:59 -04:00
2024-05-23 17:03:10 +02:00
for ( const setting of Object . keys ( settings ) ) {
2024-06-03 19:57:47 -04:00
setSetting ( this . scene , setting , settings [ setting ] ) ;
2024-05-23 17:03:10 +02:00
}
2023-10-26 16:33:59 -04:00
}
2024-06-01 14:56:32 +02:00
private loadGamepadSettings ( ) : boolean {
Object . values ( SettingGamepad ) . map ( setting = > setting as SettingGamepad ) . forEach ( setting = > setSettingGamepad ( this . scene , setting , settingGamepadDefaults [ setting ] ) ) ;
if ( ! localStorage . hasOwnProperty ( "settingsGamepad" ) ) {
return false ;
}
const settingsGamepad = JSON . parse ( localStorage . getItem ( "settingsGamepad" ) ) ;
for ( const setting of Object . keys ( settingsGamepad ) ) {
setSettingGamepad ( this . scene , setting as SettingGamepad , settingsGamepad [ setting ] ) ;
}
}
2024-02-13 18:42:11 -05:00
public saveTutorialFlag ( tutorial : Tutorial , flag : boolean ) : boolean {
2024-06-03 12:49:13 +02:00
const key = getDataTypeKey ( GameDataType . TUTORIALS ) ;
2024-02-13 18:42:11 -05:00
let tutorials : object = { } ;
2024-06-03 12:49:13 +02:00
if ( localStorage . hasOwnProperty ( key ) ) {
tutorials = JSON . parse ( localStorage . getItem ( key ) ) ;
2024-05-23 17:03:10 +02:00
}
2024-02-13 18:42:11 -05:00
Object . keys ( Tutorial ) . map ( t = > t as Tutorial ) . forEach ( t = > {
const key = Tutorial [ t ] ;
2024-05-23 17:03:10 +02:00
if ( key === tutorial ) {
2024-02-13 18:42:11 -05:00
tutorials [ key ] = flag ;
2024-05-23 17:03:10 +02:00
} else {
2024-02-13 18:42:11 -05:00
tutorials [ key ] ? ? = false ;
2024-05-23 17:03:10 +02:00
}
2024-02-13 18:42:11 -05:00
} ) ;
2024-06-03 12:49:13 +02:00
localStorage . setItem ( key , JSON . stringify ( tutorials ) ) ;
2024-02-13 18:42:11 -05:00
return true ;
}
public getTutorialFlags ( ) : TutorialFlags {
2024-06-03 12:49:13 +02:00
const key = getDataTypeKey ( GameDataType . TUTORIALS ) ;
2024-02-13 18:42:11 -05:00
const ret : TutorialFlags = { } ;
Object . values ( Tutorial ) . map ( tutorial = > tutorial as Tutorial ) . forEach ( tutorial = > ret [ Tutorial [ tutorial ] ] = false ) ;
2024-06-03 12:49:13 +02:00
if ( ! localStorage . hasOwnProperty ( key ) ) {
2024-02-13 18:42:11 -05:00
return ret ;
2024-05-23 17:03:10 +02:00
}
2024-02-13 18:42:11 -05:00
2024-06-03 12:49:13 +02:00
const tutorials = JSON . parse ( localStorage . getItem ( key ) ) ;
2024-02-13 18:42:11 -05:00
2024-05-23 17:03:10 +02:00
for ( const tutorial of Object . keys ( tutorials ) ) {
2024-02-13 18:42:11 -05:00
ret [ tutorial ] = tutorials [ tutorial ] ;
2024-05-23 17:03:10 +02:00
}
2024-02-13 18:42:11 -05:00
return ret ;
}
2024-06-03 12:49:13 +02:00
public saveSeenDialogue ( dialogue : string ) : boolean {
const key = getDataTypeKey ( GameDataType . SEEN_DIALOGUES ) ;
const dialogues : object = this . getSeenDialogues ( ) ;
dialogues [ dialogue ] = true ;
localStorage . setItem ( key , JSON . stringify ( dialogues ) ) ;
console . log ( "Dialogue saved as seen:" , dialogue ) ;
return true ;
}
public getSeenDialogues ( ) : SeenDialogues {
const key = getDataTypeKey ( GameDataType . SEEN_DIALOGUES ) ;
const ret : SeenDialogues = { } ;
if ( ! localStorage . hasOwnProperty ( key ) ) {
return ret ;
}
const dialogues = JSON . parse ( localStorage . getItem ( key ) ) ;
for ( const dialogue of Object . keys ( dialogues ) ) {
ret [ dialogue ] = dialogues [ dialogue ] ;
}
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 ,
2024-05-23 17:03:10 +02:00
trainer : scene.currentBattle.battleType === BattleType . TRAINER ? new TrainerData ( scene . currentBattle . trainer ) : null ,
2024-03-17 11:36:19 -04:00
gameVersion : scene.game.config.gameVersion ,
2024-06-08 15:07:23 +10:00
timestamp : new Date ( ) . getTime ( ) ,
challenges : scene.gameMode.challenges.map ( c = > new ChallengeData ( c ) )
2024-03-17 11:36:19 -04:00
} as SessionSaveData ;
}
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-05-23 17:03:10 +02:00
if ( slotId < 0 ) {
2024-04-20 18:46:36 -04:00
return resolve ( null ) ;
2024-05-23 17:03:10 +02:00
}
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 ;
}
} ;
2024-05-23 17:03:10 +02:00
if ( ! bypassLogin && ! localStorage . getItem ( ` sessionData ${ slotId ? slotId : "" } _ ${ loggedInUser . username } ` ) ) {
2024-06-16 02:24:50 +01:00
Utils . apiFetch ( ` savedata/session/get?slot= ${ slotId } &clientSessionId= ${ clientSessionId } ` , true )
2024-03-15 15:13:32 -04:00
. then ( response = > response . text ( ) )
. then ( async response = > {
2024-05-23 17:03:10 +02:00
if ( ! response . length || response [ 0 ] !== "{" ) {
2024-03-15 15:13:32 -04:00
console . error ( response ) ;
return resolve ( null ) ;
}
2023-04-28 15:03:42 -04:00
2024-05-23 17:03:10 +02:00
localStorage . setItem ( ` sessionData ${ slotId ? slotId : "" } _ ${ loggedInUser . username } ` , encrypt ( response , bypassLogin ) ) ;
2024-05-15 00:52:06 -04:00
2024-03-15 15:13:32 -04:00
await handleSessionData ( response ) ;
} ) ;
2024-03-15 15:56:07 -04:00
} else {
2024-05-23 17:03:10 +02:00
const sessionData = localStorage . getItem ( ` sessionData ${ slotId ? slotId : "" } _ ${ loggedInUser . username } ` ) ;
if ( sessionData ) {
2024-05-15 00:52:06 -04:00
await handleSessionData ( decrypt ( sessionData , bypassLogin ) ) ;
2024-05-23 17:03:10 +02:00
} else {
2024-03-15 15:56:07 -04:00
return resolve ( null ) ;
2024-05-23 17:03:10 +02:00
}
2024-03-15 15:56:07 -04:00
}
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-06-13 10:54:23 -04:00
const initSessionFromData = async ( sessionData : SessionSaveData ) = > {
2023-12-31 18:30:37 -05:00
console . debug ( sessionData ) ;
2023-04-28 15:03:42 -04:00
2024-06-08 15:07:23 +10:00
scene . gameMode = getGameMode ( sessionData . gameMode || GameModes . CLASSIC ) ;
if ( sessionData . challenges ) {
scene . gameMode . challenges = sessionData . challenges . map ( c = > c . toChallenge ( ) ) ;
}
2024-03-25 11:00:42 -04:00
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-05-23 17:03:10 +02:00
console . log ( "Seed:" , scene . seed ) ;
2024-03-29 20:11:52 -04:00
2024-01-11 20:27:50 -05:00
scene . sessionPlayTime = sessionData . playTime || 0 ;
2024-05-15 00:52:06 -04:00
scene . lastSavePlayTime = 0 ;
2024-01-11 20:27:50 -05:00
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
2024-05-23 17:03:10 +02:00
for ( const p of sessionData . party ) {
2023-12-31 18:30:37 -05:00
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 ;
} ) ;
2024-05-13 04:40:53 -04:00
if ( Overrides . POKEBALL_OVERRIDE . active ) {
scene . pokeballCounts = Overrides . POKEBALL_OVERRIDE . pokeballs ;
}
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-05-23 17:03:10 +02:00
if ( scene . money > this . gameStats . highestMoney ) {
2024-01-11 12:26:32 -05:00
this . gameStats . highestMoney = scene . money ;
2024-05-23 17:03:10 +02:00
}
2024-01-11 12:26:32 -05:00
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 ;
2024-05-23 17:03:10 +02:00
if ( battleType === BattleType . WILD ) {
2023-12-31 18:30:37 -05:00
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2024-05-23 17:03:10 +02:00
}
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 ;
2024-05-31 17:52:16 -05:00
scene . arena . eventTarget . dispatchEvent ( new WeatherChangedEvent ( null , scene . arena . weather ? . weatherType , scene . arena . weather ? . turnsLeft ) ) ;
scene . arena . terrain = sessionData . arena . terrain ;
scene . arena . eventTarget . dispatchEvent ( new TerrainChangedEvent ( null , scene . arena . terrain ? . terrainType , scene . arena . terrain ? . turnsLeft ) ) ;
2023-12-31 18:30:37 -05:00
// TODO
//scene.arena.tags = sessionData.arena.tags;
2023-04-28 15:03:42 -04:00
2024-05-23 17:03:10 +02:00
const modifiersModule = await import ( "../modifier/modifier" ) ;
2023-04-28 15:03:42 -04:00
2024-05-23 17:03:10 +02:00
for ( const modifierData of sessionData . modifiers ) {
2023-12-31 18:30:37 -05:00
const modifier = modifierData . toModifier ( scene , modifiersModule [ modifierData . className ] ) ;
2024-05-23 17:03:10 +02:00
if ( modifier ) {
2023-12-31 18:30:37 -05:00
scene . addModifier ( modifier , true ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-31 18:30:37 -05:00
}
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-05-23 17:03:10 +02:00
for ( const enemyModifierData of sessionData . enemyModifiers ) {
2024-03-15 15:13:32 -04:00
const modifier = enemyModifierData . toModifier ( scene , modifiersModule [ enemyModifierData . className ] ) ;
2024-05-23 17:03:10 +02:00
if ( modifier ) {
2024-03-15 15:13:32 -04:00
scene . addEnemyModifier ( modifier , true ) ;
2024-05-23 17:03:10 +02:00
}
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
} ;
2024-05-23 17:03:10 +02:00
if ( sessionData ) {
2024-03-21 13:12:05 -04:00
initSessionFromData ( sessionData ) ;
2024-05-23 17:03:10 +02:00
} else {
2024-03-21 13:12:05 -04:00
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 ) {
2024-05-23 17:03:10 +02:00
localStorage . removeItem ( ` sessionData ${ this . scene . sessionSlotId ? this . scene . sessionSlotId : "" } _ ${ loggedInUser . username } ` ) ;
2023-12-31 22:49:50 -05:00
return resolve ( true ) ;
}
updateUserInfo ( ) . then ( success = > {
2024-05-23 17:03:10 +02:00
if ( success !== null && ! success ) {
2023-12-31 22:49:50 -05:00
return resolve ( false ) ;
2024-05-23 17:03:10 +02:00
}
2024-06-16 02:24:50 +01:00
Utils . apiFetch ( ` savedata/session/delete?slot= ${ slotId } &clientSessionId= ${ clientSessionId } ` , 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-05-23 17:03:10 +02:00
localStorage . removeItem ( ` sessionData ${ this . scene . sessionSlotId ? this . scene . sessionSlotId : "" } _ ${ loggedInUser . username } ` ) ;
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 ) {
2024-05-23 17:03:10 +02:00
if ( error . startsWith ( "session out of date" ) ) {
2024-04-21 16:19:11 -04:00
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-05-14 16:42:30 -03:00
/ * D e f i n e s a l o c a l S t o r a g e i t e m ' d a i l y ' t o c h e c k o n c l e a r s , o f f l i n e i m p l e m e n t a t i o n o f s a v e d a t a / n e w c l e a r A P I
If a GameModes clear other than Daily is checked , newClear = true as usual
If a Daily mode is cleared , checks if it was already cleared before , based on seed , and returns true only to new daily clear runs * /
offlineNewClear ( scene : BattleScene ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
const sessionData = this . getSessionSaveData ( scene ) ;
const seed = sessionData . seed ;
let daily : string [ ] = [ ] ;
2024-05-23 17:03:10 +02:00
if ( sessionData . gameMode === GameModes . DAILY ) {
if ( localStorage . hasOwnProperty ( "daily" ) ) {
daily = JSON . parse ( atob ( localStorage . getItem ( "daily" ) ) ) ;
2024-05-14 16:42:30 -03:00
if ( daily . includes ( seed ) ) {
return resolve ( false ) ;
} else {
daily . push ( seed ) ;
2024-05-23 17:03:10 +02:00
localStorage . setItem ( "daily" , btoa ( JSON . stringify ( daily ) ) ) ;
2024-05-14 16:42:30 -03:00
return resolve ( true ) ;
}
} else {
daily . push ( seed ) ;
2024-05-23 17:03:10 +02:00
localStorage . setItem ( "daily" , btoa ( JSON . stringify ( daily ) ) ) ;
2024-05-14 16:42:30 -03:00
return resolve ( true ) ;
}
} else {
return resolve ( true ) ;
}
} ) ;
}
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 ) {
2024-05-23 17:03:10 +02:00
localStorage . removeItem ( ` sessionData ${ slotId ? slotId : "" } _ ${ loggedInUser . username } ` ) ;
2024-03-16 22:06:56 -04:00
return resolve ( [ true , true ] ) ;
}
updateUserInfo ( ) . then ( success = > {
2024-05-23 17:03:10 +02:00
if ( success !== null && ! success ) {
2024-03-16 22:06:56 -04:00
return resolve ( [ false , false ] ) ;
2024-05-23 17:03:10 +02:00
}
2024-03-17 11:36:19 -04:00
const sessionData = this . getSessionSaveData ( scene ) ;
2024-06-16 02:24:50 +01:00
Utils . apiPost ( ` savedata/session/clear?slot= ${ slotId } &trainerId= ${ this . trainerId } &secretId= ${ this . secretId } &clientSessionId= ${ clientSessionId } ` , JSON . stringify ( sessionData ) , undefined , true ) . then ( response = > {
2024-05-15 00:52:06 -04:00
if ( response . ok ) {
2024-03-16 22:06:56 -04:00
loggedInUser . lastSessionSlot = - 1 ;
2024-05-23 17:03:10 +02:00
localStorage . removeItem ( ` sessionData ${ this . scene . sessionSlotId ? this . scene . sessionSlotId : "" } _ ${ loggedInUser . username } ` ) ;
2024-05-15 00:52:06 -04:00
}
2024-04-21 16:19:11 -04:00
return response . json ( ) ;
} ) . then ( jsonResponse = > {
2024-05-23 17:03:10 +02:00
if ( ! jsonResponse . error ) {
2024-04-21 16:19:11 -04:00
return resolve ( [ true , jsonResponse . success as boolean ] ) ;
2024-05-23 17:03:10 +02:00
}
if ( jsonResponse && jsonResponse . error . startsWith ( "session out of date" ) ) {
2024-04-21 16:19:11 -04:00
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-05-23 17:03:10 +02:00
if ( k === "party" || k === "enemyParty" ) {
2023-12-26 14:49:23 -05:00
const ret : PokemonData [ ] = [ ] ;
2024-05-23 17:03:10 +02:00
if ( v === null ) {
2023-12-31 18:30:37 -05:00
v = [ ] ;
2024-05-23 17:03:10 +02:00
}
for ( const pd of v ) {
2023-12-26 14:49:23 -05:00
ret . push ( new PokemonData ( pd ) ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-26 14:49:23 -05:00
return ret ;
}
2024-05-23 17:03:10 +02:00
if ( k === "trainer" ) {
2023-12-26 14:49:23 -05:00
return v ? new TrainerData ( v ) : null ;
2024-05-23 17:03:10 +02:00
}
2023-12-26 14:49:23 -05:00
2024-05-23 17:03:10 +02:00
if ( k === "modifiers" || k === "enemyModifiers" ) {
const player = k === "modifiers" ;
2023-12-26 14:49:23 -05:00
const ret : PersistentModifierData [ ] = [ ] ;
2024-05-23 17:03:10 +02:00
if ( v === null ) {
2023-12-31 18:30:37 -05:00
v = [ ] ;
2024-05-23 17:03:10 +02:00
}
for ( const md of v ) {
2024-05-24 02:19:20 +02:00
if ( md ? . className === "ExpBalanceModifier" ) { // Temporarily limit EXP Balance until it gets reworked
2024-05-20 04:06:20 -05:00
md . stackCount = Math . min ( md . stackCount , 4 ) ;
2024-05-23 17:03:10 +02:00
}
2024-06-04 17:39:22 -04:00
if ( md instanceof EnemyAttackStatusEffectChanceModifier && md . effect === StatusEffect . FREEZE || md . effect === StatusEffect . SLEEP ) {
continue ;
}
2023-12-26 14:49:23 -05:00
ret . push ( new PersistentModifierData ( md , player ) ) ;
2024-05-20 04:06:20 -05:00
}
2023-12-26 14:49:23 -05:00
return ret ;
}
2024-05-23 17:03:10 +02:00
if ( k === "arena" ) {
2023-12-26 14:49:23 -05:00
return new ArenaData ( v ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-26 14:49:23 -05:00
2024-06-08 15:07:23 +10:00
if ( k === "challenges" ) {
const ret : ChallengeData [ ] = [ ] ;
if ( v === null ) {
v = [ ] ;
}
for ( const c of v ) {
ret . push ( new ChallengeData ( c ) ) ;
}
return ret ;
}
2023-12-26 14:49:23 -05:00
return v ;
} ) as SessionSaveData ;
}
2024-05-15 11:42:18 -04:00
saveAll ( scene : BattleScene , skipVerification : boolean = false , sync : boolean = false , useCachedSession : boolean = false , useCachedSystem : boolean = false ) : Promise < boolean > {
2024-05-12 23:01:05 -04:00
return new Promise < boolean > ( resolve = > {
Utils . executeIf ( ! skipVerification , updateUserInfo ) . then ( success = > {
2024-05-23 17:03:10 +02:00
if ( success !== null && ! success ) {
2024-05-12 23:01:05 -04:00
return resolve ( false ) ;
2024-05-23 17:03:10 +02:00
}
if ( sync ) {
2024-05-15 00:52:06 -04:00
this . scene . ui . savingIcon . show ( ) ;
2024-05-23 17:03:10 +02:00
}
const sessionData = useCachedSession ? this . parseSessionData ( decrypt ( localStorage . getItem ( ` sessionData ${ scene . sessionSlotId ? scene . sessionSlotId : "" } _ ${ loggedInUser . username } ` ) , bypassLogin ) ) : this . getSessionSaveData ( scene ) ;
2024-05-12 23:01:05 -04:00
const maxIntAttrValue = Math . pow ( 2 , 31 ) ;
2024-05-15 11:42:18 -04:00
const systemData = useCachedSystem ? this . parseSystemData ( decrypt ( localStorage . getItem ( ` data_ ${ loggedInUser . username } ` ) , bypassLogin ) ) : this . getSystemSaveData ( ) ;
2024-05-12 23:01:05 -04:00
const request = {
system : systemData ,
session : sessionData ,
2024-05-15 00:52:06 -04:00
sessionSlotId : scene.sessionSlotId ,
clientSessionId : clientSessionId
2024-05-12 23:01:05 -04:00
} ;
2024-05-23 17:03:10 +02:00
localStorage . setItem ( ` data_ ${ loggedInUser . username } ` , encrypt ( JSON . stringify ( systemData , ( k : any , v : any ) = > typeof v === "bigint" ? v <= maxIntAttrValue ? Number ( v ) : v . toString ( ) : v ) , bypassLogin ) ) ;
2024-05-15 00:52:06 -04:00
2024-05-23 17:03:10 +02:00
localStorage . setItem ( ` sessionData ${ scene . sessionSlotId ? scene . sessionSlotId : "" } _ ${ loggedInUser . username } ` , encrypt ( JSON . stringify ( sessionData ) , bypassLogin ) ) ;
2024-05-15 00:52:06 -04:00
2024-05-23 17:03:10 +02:00
console . debug ( "Session data saved" ) ;
2024-05-15 00:52:06 -04:00
if ( ! bypassLogin && sync ) {
2024-05-23 17:03:10 +02:00
Utils . apiPost ( "savedata/updateall" , JSON . stringify ( request , ( k : any , v : any ) = > typeof v === "bigint" ? v <= maxIntAttrValue ? Number ( v ) : v . toString ( ) : v ) , undefined , true )
2024-05-12 23:01:05 -04:00
. then ( response = > response . text ( ) )
. then ( error = > {
2024-05-15 00:52:06 -04:00
if ( sync ) {
this . scene . lastSavePlayTime = 0 ;
this . scene . ui . savingIcon . hide ( ) ;
}
2024-05-12 23:01:05 -04:00
if ( error ) {
2024-05-23 17:03:10 +02:00
if ( error . startsWith ( "client version out of date" ) ) {
2024-05-12 23:01:05 -04:00
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new OutdatedPhase ( this . scene ) ) ;
2024-05-23 17:03:10 +02:00
} else if ( error . startsWith ( "session out of date" ) ) {
2024-05-12 23:01:05 -04:00
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new ReloadSessionPhase ( this . scene ) ) ;
}
console . error ( error ) ;
return resolve ( false ) ;
}
resolve ( true ) ;
} ) ;
} else {
2024-05-15 00:52:06 -04:00
this . verify ( ) . then ( success = > {
this . scene . ui . savingIcon . hide ( ) ;
resolve ( success ) ;
} ) ;
2024-05-12 23:01:05 -04:00
}
} ) ;
} ) ;
}
2024-03-15 15:13:32 -04:00
public tryExportData ( dataType : GameDataType , slotId : integer = 0 ) : Promise < boolean > {
return new Promise < boolean > ( resolve = > {
2024-05-15 00:52:06 -04:00
const dataKey : string = ` ${ getDataTypeKey ( dataType , slotId ) } _ ${ loggedInUser . username } ` ;
2024-03-15 15:13:32 -04:00
const handleData = ( dataStr : string ) = > {
switch ( dataType ) {
2024-05-23 17:03:10 +02:00
case GameDataType . SYSTEM :
dataStr = this . convertSystemDataStr ( dataStr , true ) ;
break ;
2024-03-15 15:13:32 -04:00
}
const encryptedData = AES . encrypt ( dataStr , saveKey ) ;
2024-05-23 17:03:10 +02:00
const blob = new Blob ( [ encryptedData . toString ( ) ] , { type : "text/json" } ) ;
const link = document . createElement ( "a" ) ;
2024-03-15 15:13:32 -04:00
link . href = window . URL . createObjectURL ( blob ) ;
link . download = ` ${ dataKey } .prsv ` ;
link . click ( ) ;
link . remove ( ) ;
} ;
if ( ! bypassLogin && dataType < GameDataType . SETTINGS ) {
2024-06-16 02:24:50 +01:00
Utils . apiFetch ( ` savedata/ ${ dataType === GameDataType . SYSTEM ? "system" : "session" } /get?clientSessionId= ${ clientSessionId } ${ dataType === GameDataType . SESSION ? ` &slot= ${ slotId } ` : "" } ` , true )
2024-03-15 15:13:32 -04:00
. then ( response = > response . text ( ) )
. then ( response = > {
2024-05-23 17:03:10 +02:00
if ( ! response . length || response [ 0 ] !== "{" ) {
2024-03-15 15:13:32 -04:00
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 ) ;
2024-05-23 17:03:10 +02:00
if ( data ) {
2024-05-15 00:52:06 -04:00
handleData ( decrypt ( data , bypassLogin ) ) ;
2024-05-23 17:03:10 +02:00
}
2024-03-15 15:13:32 -04:00
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 {
2024-05-15 00:52:06 -04:00
const dataKey = ` ${ getDataTypeKey ( dataType , slotId ) } _ ${ loggedInUser . username } ` ;
2023-12-26 14:49:23 -05:00
2024-05-23 17:03:10 +02:00
let saveFile : any = document . getElementById ( "saveFile" ) ;
if ( saveFile ) {
2023-12-26 14:49:23 -05:00
saveFile . remove ( ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-24 01:45:04 +02:00
2024-05-23 17:03:10 +02:00
saveFile = document . createElement ( "input" ) ;
saveFile . id = "saveFile" ;
saveFile . type = "file" ;
saveFile . accept = ".prsv" ;
saveFile . style . display = "none" ;
saveFile . addEventListener ( "change" ,
2023-12-26 14:49:23 -05:00
e = > {
2024-05-23 17:03:10 +02:00
const reader = new FileReader ( ) ;
2023-12-26 14:49:23 -05:00
reader . onload = ( _ = > {
2024-05-23 17:03:10 +02:00
return e = > {
2024-06-13 18:44:23 -04:00
let dataName : string ;
2024-05-23 17:03:10 +02:00
let dataStr = AES . decrypt ( e . target . result . toString ( ) , saveKey ) . toString ( enc . Utf8 ) ;
let valid = false ;
try {
2024-06-13 18:44:23 -04:00
dataName = GameDataType [ dataType ] . toLowerCase ( ) ;
2023-12-26 14:49:23 -05:00
switch ( dataType ) {
2024-05-23 17:03:10 +02:00
case GameDataType . SYSTEM :
dataStr = this . convertSystemDataStr ( dataStr ) ;
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 :
case GameDataType . TUTORIALS :
valid = true ;
break ;
2023-12-26 14:49:23 -05:00
}
2024-05-23 17:03:10 +02:00
} catch ( ex ) {
console . error ( ex ) ;
}
const displayError = ( error : string ) = > this . scene . ui . showText ( error , null , ( ) = > this . scene . ui . showText ( null , 0 ) , Utils . fixedInt ( 1500 ) ) ;
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 ) ) ;
}
2024-06-13 18:44:23 -04:00
2024-05-23 17:03:10 +02:00
this . scene . ui . showText ( ` Your ${ dataName } data will be overridden and the page will reload. Proceed? ` , null , ( ) = > {
this . scene . ui . setOverlayMode ( Mode . CONFIRM , ( ) = > {
localStorage . setItem ( dataKey , encrypt ( dataStr , bypassLogin ) ) ;
if ( ! bypassLogin && dataType < GameDataType . SETTINGS ) {
updateUserInfo ( ) . then ( success = > {
2024-06-17 02:44:07 +01:00
if ( ! success [ 0 ] ) {
2024-05-23 17:03:10 +02:00
return displayError ( ` Could not contact the server. Your ${ dataName } data could not be imported. ` ) ;
}
2024-06-16 02:24:50 +01:00
let url : string ;
if ( dataType === GameDataType . SESSION ) {
url = ` savedata/session/update?slot= ${ slotId } &trainerId= ${ this . trainerId } &secretId= ${ this . secretId } &clientSessionId= ${ clientSessionId } ` ;
} else {
url = ` savedata/system/update?trainerId= ${ this . trainerId } &secretId= ${ this . secretId } &clientSessionId= ${ clientSessionId } ` ;
}
Utils . apiPost ( url , dataStr , undefined , true )
2024-05-23 17:03:10 +02: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 {
window . location = window . location ;
}
} , ( ) = > {
this . scene . ui . revertMode ( ) ;
this . scene . ui . showText ( null , 0 ) ;
} , false , - 98 ) ;
} ) ;
} ;
} ) ( ( e . target as any ) . files [ 0 ] ) ;
2023-12-26 14:49:23 -05:00
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 = { } ;
2024-05-23 17:03:10 +02:00
for ( const 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-05-23 17:03:10 +02:00
for ( let s = 0 ; s < defaultStarterSpecies . length ; s ++ ) {
2024-02-17 00:40:03 -05:00
defaultStarterNatures . push ( Utils . randSeedItem ( neutralNatures ) ) ;
2024-05-23 17:03:10 +02:00
}
} , 0 , "default" ) ;
2024-01-05 22:24:05 -05:00
2024-04-19 23:37:23 -04:00
for ( let ds = 0 ; ds < defaultStarterSpecies . length ; ds ++ ) {
2024-05-23 17:03:10 +02:00
const 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 ) ;
2024-05-23 17:03:10 +02:00
for ( const i in entry . ivs ) {
2023-11-12 23:47:04 -05:00
entry . ivs [ i ] = 10 ;
2024-05-23 17:03:10 +02:00
}
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-05-23 17:03:10 +02:00
for ( const speciesId of starterSpeciesIds ) {
2024-04-13 18:59:58 -04:00
starterData [ speciesId ] = {
moveset : null ,
eggMoves : 0 ,
candyCount : 0 ,
2024-05-02 17:48:08 -04:00
friendship : 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 ,
2024-05-07 00:44:41 -04:00
valueReduction : 0 ,
classicWinCount : 0
2024-04-13 18:59:58 -04:00
} ;
}
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-05-23 17:03:10 +02:00
if ( ! trainer && pokemon . species . subLegendary ) {
2024-05-05 17:11:29 -04:00
this . gameStats . subLegendaryPokemonSeen ++ ;
2024-05-23 17:03:10 +02:00
} else if ( ! trainer && pokemon . species . legendary ) {
2024-04-25 13:20:32 -04:00
this . gameStats . legendaryPokemonSeen ++ ;
2024-05-23 17:03:10 +02:00
} else if ( ! trainer && pokemon . species . mythical ) {
2024-04-25 13:20:32 -04:00
this . gameStats . mythicalPokemonSeen ++ ;
2024-05-23 17:03:10 +02:00
}
if ( ! trainer && pokemon . isShiny ( ) ) {
2024-01-11 12:26:32 -05:00
this . gameStats . shinyPokemonSeen ++ ;
2024-05-23 17:03:10 +02:00
}
2024-01-11 12:26:32 -05:00
}
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 ;
2024-05-23 17:03:10 +02:00
if ( noStarterFormKeys . includes ( pokemon . getFormKey ( ) ) ) {
2024-04-05 22:58:40 -04:00
pokemon . formIndex = 0 ;
2024-05-23 17:03:10 +02:00
}
2024-04-05 22:58:40 -04:00
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-05-24 01:45:04 +02:00
2024-04-13 18:59:58 -04:00
const hasPrevolution = pokemonPrevolutions . hasOwnProperty ( species . speciesId ) ;
const newCatch = ! caughtAttr ;
2024-05-02 17:48:08 -04:00
const hasNewAttr = ( caughtAttr & dexAttr ) !== dexAttr ;
2024-04-13 18:59:58 -04:00
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 ++ ;
2024-05-23 17:03:10 +02:00
if ( pokemon . species . subLegendary ) {
2024-05-05 17:11:29 -04:00
this . gameStats . subLegendaryPokemonCaught ++ ;
2024-05-23 17:03:10 +02:00
} else if ( pokemon . species . legendary ) {
2024-01-11 12:26:32 -05:00
this . gameStats . legendaryPokemonCaught ++ ;
2024-05-23 17:03:10 +02:00
} else if ( pokemon . species . mythical ) {
2024-01-11 12:26:32 -05:00
this . gameStats . mythicalPokemonCaught ++ ;
2024-05-23 17:03:10 +02:00
}
if ( pokemon . isShiny ( ) ) {
2024-01-11 12:26:32 -05:00
this . gameStats . shinyPokemonCaught ++ ;
2024-05-23 17:03:10 +02:00
}
2024-01-11 12:26:32 -05:00
} else {
2023-12-31 13:20:28 -05:00
dexEntry . hatchedCount ++ ;
2024-01-11 12:26:32 -05:00
this . gameStats . pokemonHatched ++ ;
2024-05-23 17:03:10 +02:00
if ( pokemon . species . subLegendary ) {
2024-05-05 17:11:29 -04:00
this . gameStats . subLegendaryPokemonHatched ++ ;
2024-05-23 17:03:10 +02:00
} else if ( pokemon . species . legendary ) {
2024-01-11 12:26:32 -05:00
this . gameStats . legendaryPokemonHatched ++ ;
2024-05-23 17:03:10 +02:00
} else if ( pokemon . species . mythical ) {
2024-01-11 12:26:32 -05:00
this . gameStats . mythicalPokemonHatched ++ ;
2024-05-23 17:03:10 +02:00
}
if ( pokemon . isShiny ( ) ) {
2024-01-11 12:26:32 -05:00
this . gameStats . shinyPokemonHatched ++ ;
2024-05-23 17:03:10 +02:00
}
2024-01-11 12:26:32 -05:00
}
2023-04-18 01:32:26 -04:00
2024-05-23 17:03:10 +02:00
if ( ! hasPrevolution && ( ! pokemon . scene . gameMode . isDaily || hasNewAttr || fromEgg ) ) {
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-05-23 17:03:10 +02:00
}
2024-04-13 18:59:58 -04:00
}
2024-05-24 01:45:04 +02: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 ( ) ) ;
2024-05-23 17:03:10 +02:00
} else {
2023-12-01 17:23:26 -05:00
resolve ( ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-01 17:23:26 -05:00
} ;
2023-04-18 01:32:26 -04:00
2023-12-01 17:23:26 -05:00
if ( newCatch && speciesStarters . hasOwnProperty ( species . speciesId ) ) {
2024-05-23 17:03:10 +02: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 ) ;
2024-05-23 17:03:10 +02:00
} else {
2023-12-01 17:23:26 -05:00
checkPrevolution ( ) ;
2024-05-23 17:03:10 +02:00
}
2023-04-18 01:32:26 -04:00
} ) ;
}
2024-05-07 00:44:41 -04:00
incrementRibbonCount ( species : PokemonSpecies , forStarter : boolean = false ) : integer {
const speciesIdToIncrement : Species = species . getRootSpeciesId ( forStarter ) ;
if ( ! this . starterData [ speciesIdToIncrement ] . classicWinCount ) {
this . starterData [ speciesIdToIncrement ] . classicWinCount = 0 ;
}
2024-05-24 01:45:04 +02:00
2024-05-23 17:03:10 +02:00
if ( ! this . starterData [ speciesIdToIncrement ] . classicWinCount ) {
2024-05-07 00:44:41 -04:00
this . scene . gameData . gameStats . ribbonsOwned ++ ;
2024-05-23 17:03:10 +02:00
}
2024-05-07 00:44:41 -04:00
const ribbonsInStats : integer = this . scene . gameData . gameStats . ribbonsOwned ;
2024-05-23 17:03:10 +02:00
if ( ribbonsInStats >= 100 ) {
2024-05-07 00:44:41 -04:00
this . scene . validateAchv ( achvs . _100_RIBBONS ) ;
2024-05-23 17:03:10 +02:00
}
if ( ribbonsInStats >= 75 ) {
2024-05-07 00:44:41 -04:00
this . scene . validateAchv ( achvs . _75_RIBBONS ) ;
2024-05-23 17:03:10 +02:00
}
if ( ribbonsInStats >= 50 ) {
2024-05-07 00:44:41 -04:00
this . scene . validateAchv ( achvs . _50_RIBBONS ) ;
2024-05-23 17:03:10 +02:00
}
if ( ribbonsInStats >= 25 ) {
2024-05-07 00:44:41 -04:00
this . scene . validateAchv ( achvs . _25_RIBBONS ) ;
2024-05-23 17:03:10 +02:00
}
if ( ribbonsInStats >= 10 ) {
2024-05-07 00:44:41 -04:00
this . scene . validateAchv ( achvs . _10_RIBBONS ) ;
2024-05-23 17:03:10 +02:00
}
2024-05-07 00:44:41 -04:00
return ++ this . starterData [ speciesIdToIncrement ] . classicWinCount ;
}
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-05-23 17:03:10 +02:00
if ( ! this . starterData [ speciesId ] . eggMoves ) {
2024-04-13 18:59:58 -04:00
this . starterData [ speciesId ] . eggMoves = 0 ;
2024-05-23 17:03:10 +02:00
}
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-05-23 17:03:10 +02:00
this . scene . playSound ( "level_up_fanfare" ) ;
2024-06-24 09:14:18 +09:00
const moveName = allMoves [ speciesEggMoves [ speciesId ] [ eggMoveIndex ] ] . name ;
this . scene . ui . showText ( eggMoveIndex === 3 ? i18next . t ( "egg:rareEggMoveUnlock" , { moveName : moveName } ) : i18next . t ( "egg:eggMoveUnlock" , { moveName : moveName } ) , null , ( ) = > resolve ( true ) , null , true ) ;
2024-02-25 12:45:41 -05:00
} ) ;
}
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 ++ ) {
2024-05-23 17:03:10 +02:00
if ( dexIvs [ i ] < ivs [ i ] ) {
2023-12-19 23:51:48 -05:00
dexIvs [ i ] = ivs [ i ] ;
2024-05-23 17:03:10 +02:00
}
2023-12-19 23:51:48 -05:00
}
2024-05-23 17:03:10 +02:00
if ( dexIvs . filter ( iv = > iv === 31 ) . length === 6 ) {
2023-12-19 23:51:48 -05:00
this . scene . validateAchv ( achvs . PERFECT_IVS ) ;
2024-05-23 17:03:10 +02:00
}
2023-12-19 23:51:48 -05:00
} 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 ;
2024-05-23 17:03:10 +02:00
for ( const s of dexKeys ) {
if ( dexEntryPredicate ( this . dexData [ s ] ) ) {
2024-04-05 22:58:40 -04:00
speciesCount ++ ;
2024-05-23 17:03:10 +02:00
}
2024-04-05 22:58:40 -04:00
}
return speciesCount ;
}
getStarterCount ( dexEntryPredicate : ( entry : DexEntry ) = > boolean ) : integer {
const starterKeys = Object . keys ( speciesStarters ) ;
let starterCount = 0 ;
2024-05-23 17:03:10 +02:00
for ( const s of starterKeys ) {
2024-04-05 22:58:40 -04:00
const starterDexEntry = this . dexData [ s ] ;
2024-05-23 17:03:10 +02:00
if ( dexEntryPredicate ( starterDexEntry ) ) {
2024-04-05 22:58:40 -04:00
starterCount ++ ;
2024-05-23 17:03:10 +02:00
}
2024-04-05 22:58:40 -04:00
}
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 ++ ) {
2024-05-23 17:03:10 +02:00
if ( dexEntry . natureAttr & Math . pow ( 2 , n + 1 ) ) {
2024-01-05 22:24:05 -05:00
return n as Nature ;
2024-05-23 17:03:10 +02:00
}
2024-01-05 22:24:05 -05:00
}
return 0 as Nature ;
}
getSpeciesDefaultNatureAttr ( species : PokemonSpecies ) : integer {
return Math . pow ( 2 , this . getSpeciesDefaultNature ( species ) ) ;
}
2024-04-26 17:32:28 -04:00
getDexAttrLuck ( dexAttr : bigint ) : integer {
return dexAttr & DexAttr . SHINY ? dexAttr & DexAttr . VARIANT_3 ? 3 : dexAttr & DexAttr . VARIANT_2 ? 2 : 1 : 0 ;
}
2024-01-05 22:24:05 -05:00
getNaturesForAttr ( natureAttr : integer ) : Nature [ ] {
2024-05-23 17:03:10 +02:00
const ret : Nature [ ] = [ ] ;
2024-01-05 22:24:05 -05:00
for ( let n = 0 ; n < 25 ; n ++ ) {
2024-05-23 17:03:10 +02:00
if ( natureAttr & Math . pow ( 2 , n + 1 ) ) {
2024-01-05 22:24:05 -05:00
ret . push ( n ) ;
2024-05-23 17:03:10 +02:00
}
2024-01-05 22:24:05 -05:00
}
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 ) = > {
2024-05-23 17:03:10 +02:00
if ( value > 1 ) {
2024-02-12 16:38:46 -05:00
value -- ;
2024-05-23 17:03:10 +02:00
} else {
2024-02-12 16:38:46 -05:00
value /= 2 ;
2024-05-23 17:03:10 +02:00
}
2024-02-12 16:38:46 -05:00
return value ;
2024-05-23 17:03:10 +02:00
} ;
2024-02-12 16:38:46 -05:00
2024-05-23 17:03:10 +02:00
for ( let v = 0 ; v < this . starterData [ speciesId ] . valueReduction ; v ++ ) {
2024-02-12 16:38:46 -05:00
value = decrementValue ( value ) ;
2024-05-23 17:03:10 +02:00
}
2024-02-12 16:38:46 -05:00
return value ;
}
2023-11-12 23:47:04 -05:00
getFormIndex ( attr : bigint ) : integer {
2024-05-23 17:03:10 +02:00
if ( ! attr || attr < DexAttr . DEFAULT_FORM ) {
2023-11-12 23:47:04 -05:00
return 0 ;
2024-05-23 17:03:10 +02:00
}
2023-11-12 23:47:04 -05:00
let f = 0 ;
2024-05-23 17:03:10 +02:00
while ( ! ( attr & this . getFormAttr ( f ) ) ) {
2023-11-12 23:47:04 -05:00
f ++ ;
2024-05-23 17:03:10 +02:00
}
2023-11-12 23:47:04 -05:00
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
}
2024-05-24 01:45:04 +02:00
2023-12-31 13:20:28 -05:00
consolidateDexData ( dexData : DexData ) : void {
2024-05-23 17:03:10 +02:00
for ( const k of Object . keys ( dexData ) ) {
2023-12-31 13:20:28 -05:00
const entry = dexData [ k ] as DexEntry ;
2024-05-23 17:03:10 +02:00
if ( ! entry . hasOwnProperty ( "hatchedCount" ) ) {
2023-12-31 13:20:28 -05:00
entry . hatchedCount = 0 ;
2024-05-23 17:03:10 +02:00
}
if ( ! entry . hasOwnProperty ( "natureAttr" ) || ( entry . caughtAttr && ! entry . natureAttr ) ) {
2024-01-05 22:24:05 -05:00
entry . natureAttr = this . defaultDexData [ k ] . natureAttr || Math . pow ( 2 , Utils . randInt ( 25 , 1 ) ) ;
2024-05-23 17:03:10 +02:00
}
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 ;
2024-05-23 17:03:10 +02:00
for ( const s of starterIds ) {
2024-04-18 22:52:26 -04:00
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 ) {
2024-05-23 17:03:10 +02:00
if ( ! ( dexAttr & DexAttr . DEFAULT_VARIANT ) ) {
2024-04-18 22:52:26 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . DEFAULT_VARIANT ;
2024-05-23 17:03:10 +02:00
}
if ( dexAttr & DexAttr . VARIANT_2 ) {
2024-04-18 22:52:26 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
2024-05-23 17:03:10 +02:00
}
if ( dexAttr & DexAttr . VARIANT_3 ) {
2024-04-18 22:52:26 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
2024-05-23 17:03:10 +02:00
}
2024-04-18 22:52:26 -04:00
}
}
}
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 ] ) ) {
2024-05-23 17:03:10 +02:00
for ( const s of starterIds ) {
2024-04-20 22:30:28 -04:00
const species = getPokemonSpecies ( s ) ;
if ( variantData [ s ] ) {
const tempCaughtAttr = dexData [ s ] . caughtAttr ;
let seenVariant2 = false ;
let seenVariant3 = false ;
2024-05-23 17:03:10 +02:00
const checkEvoSpecies = ( es : Species ) = > {
2024-04-20 22:30:28 -04:00
seenVariant2 || = ! ! ( dexData [ es ] . seenAttr & DexAttr . VARIANT_2 ) ;
seenVariant3 || = ! ! ( dexData [ es ] . seenAttr & DexAttr . VARIANT_3 ) ;
if ( pokemonEvolutions . hasOwnProperty ( es ) ) {
2024-05-23 17:03:10 +02:00
for ( const pe of pokemonEvolutions [ es ] ) {
2024-04-20 22:30:28 -04:00
checkEvoSpecies ( pe . speciesId ) ;
2024-05-23 17:03:10 +02:00
}
2024-04-20 22:30:28 -04:00
}
} ;
checkEvoSpecies ( s ) ;
2024-05-23 17:03:10 +02:00
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_2 && ! seenVariant2 ) {
2024-04-20 22:30:28 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
2024-05-23 17:03:10 +02:00
}
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_3 && ! seenVariant3 ) {
2024-04-20 22:30:28 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
2024-05-23 17:03:10 +02:00
}
2024-04-20 22:30:28 -04:00
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 ;
2024-05-23 17:03:10 +02:00
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_2 ) {
2024-04-20 22:30:28 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_2 ;
2024-05-23 17:03:10 +02:00
}
if ( dexData [ s ] . caughtAttr & DexAttr . VARIANT_3 ) {
2024-04-20 22:30:28 -04:00
dexData [ s ] . caughtAttr ^= DexAttr . VARIANT_3 ;
2024-05-23 17:03:10 +02:00
}
2024-04-20 22:30:28 -04:00
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-05-24 01:45:04 +02:00
2024-04-23 22:16:49 -04:00
fixStarterData ( systemData : SystemSaveData ) : void {
2024-05-23 17:03:10 +02:00
for ( const starterId of defaultStarterSpecies ) {
2024-04-23 22:16:49 -04:00
systemData . starterData [ starterId ] . abilityAttr |= AbilityAttr . ABILITY_1 ;
2024-05-23 17:03:10 +02:00
}
2024-04-23 22:16:49 -04:00
}
2024-05-05 17:11:29 -04:00
fixLegendaryStats ( systemData : SystemSaveData ) : void {
systemData . gameStats . subLegendaryPokemonSeen = 0 ;
systemData . gameStats . subLegendaryPokemonCaught = 0 ;
systemData . gameStats . subLegendaryPokemonHatched = 0 ;
allSpecies . filter ( s = > s . subLegendary ) . forEach ( s = > {
const dexEntry = systemData . dexData [ s . speciesId ] ;
systemData . gameStats . subLegendaryPokemonSeen += dexEntry . seenCount ;
systemData . gameStats . legendaryPokemonSeen = Math . max ( systemData . gameStats . legendaryPokemonSeen - dexEntry . seenCount , 0 ) ;
systemData . gameStats . subLegendaryPokemonCaught += dexEntry . caughtCount ;
systemData . gameStats . legendaryPokemonCaught = Math . max ( systemData . gameStats . legendaryPokemonCaught - dexEntry . caughtCount , 0 ) ;
systemData . gameStats . subLegendaryPokemonHatched += dexEntry . hatchedCount ;
systemData . gameStats . legendaryPokemonHatched = Math . max ( systemData . gameStats . legendaryPokemonHatched - dexEntry . hatchedCount , 0 ) ;
} ) ;
systemData . gameStats . subLegendaryPokemonSeen = Math . max ( systemData . gameStats . subLegendaryPokemonSeen , systemData . gameStats . subLegendaryPokemonCaught ) ;
systemData . gameStats . legendaryPokemonSeen = Math . max ( systemData . gameStats . legendaryPokemonSeen , systemData . gameStats . legendaryPokemonCaught ) ;
systemData . gameStats . mythicalPokemonSeen = Math . max ( systemData . gameStats . mythicalPokemonSeen , systemData . gameStats . mythicalPokemonCaught ) ;
}
2024-05-23 17:03:10 +02:00
}