Compare commits

...

11 Commits

Author SHA1 Message Date
Madmadness65
7061bb0d70 Lower starter cost of some mythical Pokémon
Deoxys, Victini, and Genesect should not be in the Legendary egg pool, to make them consistent with the other mythicals.
Also additional learnset changes for a couple mythicals with no/lacking starting moves.
2024-05-14 01:21:28 -05:00
Madmadness65
37f672789d Give Delibird an actual level up learnset
No longer is it stuck with just two moves via level up. It now has a thematic set of level up moves based on other similar Pokémon and its TM compatibility.
2024-05-14 00:56:45 -05:00
Madmadness65
856757fd56 Add custom Jungle biome music by Lmz 2024-05-13 23:46:28 -05:00
James Diefenbach
81349816d9
changed formIndex to getFormkey in EvolutionItem constructor (#844)
Co-authored-by: James Diefenbach <z5421232@ad.unsw.edu.au>
2024-05-13 23:01:08 -05:00
AJ Fontaine
fe7a4180a2
Give level 1 moves back that were made unusable in SV (#842) 2024-05-13 21:21:49 -05:00
Lugiad
c0426cfe6b
Update French move.ts (#845) 2024-05-13 21:19:28 -05:00
Madmadness65
4e279224c5 Update Let's Go moves to their Gen 8 functionality
This primarily just affects Zippy Zap, as it loses its always crit attribute for an evasion increase (Associated localization strings will need updating). Also implemented Sparkly Swirl's status curing attribute.

Also included is a wild evolution delay for some stone evolution Pokémon that was previously overlooked.
2024-05-13 20:59:38 -05:00
Lugiad
c42441294b
Update French ability-trigger.ts (#836) 2024-05-13 20:17:41 -05:00
Douglas Marchione de Souza
de3ca08704
Fix Air_Lock ability to express when it is switched in (#765)
* Airlock ability now properly announces when it is
in field when the pokemon with it is switched in

* Set message as in the original games, also created
a new attribute for in switch messages to not have
the pokemon name required to be on it.

* Committing language changes (i18n function)

* Revert "Committing language changes (i18n function)"

This reverts commit 2a3152003b.

* Changed message variable name
2024-05-13 20:09:46 -05:00
Flashfyre
7943633481 Revert "Implement hybrid saving for optimization"
This reverts commit b45cd2f7e7.
2024-05-13 20:56:28 -04:00
Flashfyre
ab92bc61ba Revert "Only show save icon on server sync"
This reverts commit 5d0dbfff98.
2024-05-13 20:56:19 -04:00
19 changed files with 309 additions and 76 deletions

View File

@ -44,7 +44,8 @@ Check out our [Trello Board](https://trello.com/b/z10B703R/pokerogue-board) to s
- Arata Iiyoshi - Arata Iiyoshi
- Atsuhiro Ishizuna - Atsuhiro Ishizuna
- Pokémon Black/White 2 - Pokémon Black/White 2
- Firel (Additional biome themes) - Firel (Custom Metropolis and Laboratory biome music)
- Lmz (Custom Jungle biome music)
- edifette (Title screen music) - edifette (Title screen music)
### 🎵 Sound Effects ### 🎵 Sound Effects

Binary file not shown.

View File

@ -1407,6 +1407,23 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr {
} }
} }
export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr {
//Attr doesn't force pokemon name on the message
private message: string;
constructor(message: string) {
super(true);
this.message = message;
}
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
pokemon.scene.queueMessage(this.message);
return true;
}
}
export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr {
private tagType: BattlerTagType; private tagType: BattlerTagType;
private turnCount: integer; private turnCount: integer;
@ -3061,7 +3078,8 @@ export function initAbilities() {
.attr(BlockCritAbAttr) .attr(BlockCritAbAttr)
.ignorable(), .ignorable(),
new Ability(Abilities.AIR_LOCK, 3) new Ability(Abilities.AIR_LOCK, 3)
.attr(SuppressWeatherEffectAbAttr, true), .attr(SuppressWeatherEffectAbAttr, true)
.attr(PostSummonUnnamedMessageAbAttr, "The effects of the weather disappeared."),
new Ability(Abilities.TANGLED_FEET, 4) new Ability(Abilities.TANGLED_FEET, 4)
.conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), BattleStatMultiplierAbAttr, BattleStat.EVA, 2) .conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), BattleStatMultiplierAbAttr, BattleStat.EVA, 2)
.ignorable(), .ignorable(),

View File

@ -6221,7 +6221,7 @@ export function initMoves() {
.ignoresVirtual(), .ignoresVirtual(),
/* End Unused */ /* End Unused */
new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 100, 2, 7) new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 100, 2, 7)
.attr(CritOnlyAttr), .attr(StatChangeAttr, BattleStat.EVA, 1, true),
new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7) new AttackMove(Moves.SPLISHY_SPLASH, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 30, 0, 7)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS) .attr(StatusEffectAttr, StatusEffect.PARALYSIS)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
@ -6246,7 +6246,7 @@ export function initMoves() {
new AttackMove(Moves.FREEZY_FROST, Type.ICE, MoveCategory.SPECIAL, 100, 90, 10, -1, 0, 7) new AttackMove(Moves.FREEZY_FROST, Type.ICE, MoveCategory.SPECIAL, 100, 90, 10, -1, 0, 7)
.attr(ResetStatsAttr), .attr(ResetStatsAttr),
new AttackMove(Moves.SPARKLY_SWIRL, Type.FAIRY, MoveCategory.SPECIAL, 120, 85, 5, -1, 0, 7) new AttackMove(Moves.SPARKLY_SWIRL, Type.FAIRY, MoveCategory.SPECIAL, 120, 85, 5, -1, 0, 7)
.partial(), .attr(PartyStatusCureAttr, null, Abilities.NONE),
new AttackMove(Moves.VEEVEE_VOLLEY, Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 20, -1, 0, 7) new AttackMove(Moves.VEEVEE_VOLLEY, Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 20, -1, 0, 7)
.attr(FriendshipPowerAttr), .attr(FriendshipPowerAttr),
new AttackMove(Moves.DOUBLE_IRON_BASH, Type.STEEL, MoveCategory.PHYSICAL, 60, 100, 5, 30, 0, 7) new AttackMove(Moves.DOUBLE_IRON_BASH, Type.STEEL, MoveCategory.PHYSICAL, 60, 100, 5, 30, 0, 7)

View File

@ -1385,10 +1385,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HELIOLISK, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.HELIOLISK, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.CHARJABUG]: [ [Species.CHARJABUG]: [
new SpeciesEvolution(Species.VIKAVOLT, 1, EvolutionItem.THUNDER_STONE, null) new SpeciesEvolution(Species.VIKAVOLT, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.CRABRAWLER]: [ [Species.CRABRAWLER]: [
new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null) new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ROCKRUFF]: [ [Species.ROCKRUFF]: [
new SpeciesFormEvolution(Species.LYCANROC, '', 'midday', 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0)), null), new SpeciesFormEvolution(Species.LYCANROC, '', 'midday', 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0)), null),

File diff suppressed because it is too large Load Diff

View File

@ -2759,7 +2759,7 @@ export const speciesStarters = {
[Species.GROUDON]: 8, [Species.GROUDON]: 8,
[Species.RAYQUAZA]: 8, [Species.RAYQUAZA]: 8,
[Species.JIRACHI]: 7, [Species.JIRACHI]: 7,
[Species.DEOXYS]: 8, [Species.DEOXYS]: 7,
[Species.TURTWIG]: 3, [Species.TURTWIG]: 3,
[Species.CHIMCHAR]: 3, [Species.CHIMCHAR]: 3,
@ -2813,7 +2813,7 @@ export const speciesStarters = {
[Species.DARKRAI]: 7, [Species.DARKRAI]: 7,
[Species.SHAYMIN]: 7, [Species.SHAYMIN]: 7,
[Species.ARCEUS]: 9, [Species.ARCEUS]: 9,
[Species.VICTINI]: 8, [Species.VICTINI]: 7,
[Species.SNIVY]: 3, [Species.SNIVY]: 3,
[Species.TEPIG]: 3, [Species.TEPIG]: 3,
@ -2895,7 +2895,7 @@ export const speciesStarters = {
[Species.KYUREM]: 8, [Species.KYUREM]: 8,
[Species.KELDEO]: 7, [Species.KELDEO]: 7,
[Species.MELOETTA]: 7, [Species.MELOETTA]: 7,
[Species.GENESECT]: 8, [Species.GENESECT]: 7,
[Species.CHESPIN]: 3, [Species.CHESPIN]: 3,
[Species.FENNEKIN]: 3, [Species.FENNEKIN]: 3,

View File

@ -617,7 +617,7 @@ export class Arena {
case Biome.CONSTRUCTION_SITE: case Biome.CONSTRUCTION_SITE:
return 1.222; return 1.222;
case Biome.JUNGLE: case Biome.JUNGLE:
return 2.477; return 0.000;
case Biome.FAIRY_CAVE: case Biome.FAIRY_CAVE:
return 4.542; return 4.542;
case Biome.TEMPLE: case Biome.TEMPLE:

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
"zippyZap": { "zippyZap": {
name: "Britzelturbo", name: "Britzelturbo",
effect: "Ein stürmischer Blitz-Angriff mit hoher Erstschlag- und Volltrefferquote." effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness."
}, },
"splishySplash": { "splishySplash": {
name: "Plätschersurfer", name: "Plätschersurfer",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
"zippyZap": { "zippyZap": {
name: "Zippy Zap", name: "Zippy Zap",
effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and results in a critical hit." effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness."
}, },
"splishySplash": { "splishySplash": {
name: "Splishy Splash", name: "Splishy Splash",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
zippyZap: { zippyZap: {
name: "Pikaturbo", name: "Pikaturbo",
effect: "Ataque eléctrico a la velocidad del rayo. Este movimiento tiene prioridad alta y propina golpes críticos.", effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness.",
}, },
splishySplash: { splishySplash: {
name: "Salpikasurf", name: "Salpikasurf",

View File

@ -1,5 +1,5 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n"; import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = { export const abilityTriggers: SimpleTranslationEntries = {
'blockRecoilDamage' : `{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!`, 'blockRecoilDamage' : `{{abilityName}}\nde {{pokemonName}} le protège du contrecoup !`,
} as const; } as const;

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
"zippyZap": { "zippyZap": {
name: "Pika-Sprint", name: "Pika-Sprint",
effect: "Une attaque électrique rapide comme léclair qui inflige un coup critique à coup sûr. Frappe en priorité." effect: "Une attaque électrique rapide comme léclair qui auguemente lesquive. Frappe en priorité."
}, },
"splishySplash": { "splishySplash": {
name: "Pika-Splash", name: "Pika-Splash",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
zippyZap: { zippyZap: {
name: "Sprintaboom", name: "Sprintaboom",
effect: "Un attacco elettrico ad altissima velocità. Questa mossa ha priorità alta e infligge sicuramente un brutto colpo.", effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness.",
}, },
splishySplash: { splishySplash: {
name: "Surfasplash", name: "Surfasplash",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
zippyZap: { zippyZap: {
name: "Zippy Zap", name: "Zippy Zap",
effect: "O usuário ataca o alvo com rajadas de eletricidade em alta velocidade. Este movimento sempre ataca primeiro e resulta em um golpe crítico." effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness."
}, },
splishySplash: { splishySplash: {
name: "Splishy Splash", name: "Splishy Splash",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
}, },
"zippyZap": { "zippyZap": {
name: "电电加速", name: "电电加速",
effect: "迅猛无比的电击。必定能够\n先制攻击击中对方的要害", effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness.",
}, },
"splishySplash": { "splishySplash": {
name: "滔滔冲浪", name: "滔滔冲浪",

View File

@ -554,10 +554,10 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
super(Utils.toReadableString(EvolutionItem[evolutionItem]), `Causes certain Pokémon to evolve`, (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id), super(Utils.toReadableString(EvolutionItem[evolutionItem]), `Causes certain Pokémon to evolve`, (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem
&& (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.formIndex == 0)) && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX))
return null; return null;
else if (pokemon.isFusion() && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem else if (pokemon.isFusion() && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem
&& (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.fusionFormIndex == 0)) && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX))
return null; return null;
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;

View File

@ -775,7 +775,7 @@ export class EncounterPhase extends BattlePhase {
this.scene.ui.setMode(Mode.MESSAGE).then(() => { this.scene.ui.setMode(Mode.MESSAGE).then(() => {
if (!this.loaded) { if (!this.loaded) {
this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 5 === 1).then(success => { this.scene.gameData.saveAll(this.scene, true).then(success => {
this.scene.disableMenu = false; this.scene.disableMenu = false;
if (!success) if (!success)
return this.scene.reset(true); return this.scene.reset(true);

View File

@ -277,7 +277,7 @@ export class GameData {
const maxIntAttrValue = Math.pow(2, 31); const maxIntAttrValue = Math.pow(2, 31);
const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v); const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v);
if (!bypassLogin && !localStorage.getItem('data')) { if (!bypassLogin) {
Utils.apiPost(`savedata/update?datatype=${GameDataType.SYSTEM}`, systemData, undefined, true) Utils.apiPost(`savedata/update?datatype=${GameDataType.SYSTEM}`, systemData, undefined, true)
.then(response => response.text()) .then(response => response.text())
.then(error => { .then(error => {
@ -293,15 +293,12 @@ export class GameData {
console.error(error); console.error(error);
return resolve(false); return resolve(false);
} }
localStorage.removeItem('data');
resolve(true); resolve(true);
}); });
} else { } else {
const encFunc = bypassLogin localStorage.setItem('data_bak', localStorage.getItem('data'));
? (data: string) => btoa(data)
: (data: string) => AES.encrypt(data, saveKey);
localStorage.setItem('data', encFunc(systemData)); localStorage.setItem('data', btoa(systemData));
this.scene.ui.savingIcon.hide(); this.scene.ui.savingIcon.hide();
@ -312,7 +309,7 @@ export class GameData {
public loadSystem(): Promise<boolean> { public loadSystem(): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
if (bypassLogin && !localStorage.getItem('data')) if (bypassLogin && !localStorage.hasOwnProperty('data'))
return resolve(false); return resolve(false);
const handleSystemData = (systemDataStr: string) => { const handleSystemData = (systemDataStr: string) => {
@ -424,7 +421,7 @@ export class GameData {
} }
} }
if (!bypassLogin && !localStorage.getItem('data')) { if (!bypassLogin) {
Utils.apiFetch(`savedata/get?datatype=${GameDataType.SYSTEM}`, true) Utils.apiFetch(`savedata/get?datatype=${GameDataType.SYSTEM}`, true)
.then(response => response.text()) .then(response => response.text())
.then(response => { .then(response => {
@ -442,12 +439,8 @@ export class GameData {
handleSystemData(response); handleSystemData(response);
}); });
} else { } else
const decFunc = bypassLogin handleSystemData(atob(localStorage.getItem('data')));
? (data: string) => atob(data)
: (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8);
handleSystemData(decFunc(localStorage.getItem('data')));
}
}); });
} }
@ -564,6 +557,40 @@ export class GameData {
} as SessionSaveData; } as SessionSaveData;
} }
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);
const sessionData = this.getSessionSaveData(scene);
if (!bypassLogin) {
Utils.apiPost(`savedata/update?datatype=${GameDataType.SESSION}&slot=${scene.sessionSlotId}&trainerId=${this.trainerId}&secretId=${this.secretId}`, JSON.stringify(sessionData), undefined, true)
.then(response => response.text())
.then(error => {
if (error) {
if (error.startsWith('session out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
}
console.error(error);
return resolve(false);
}
console.debug('Session data saved');
resolve(true);
});
} else {
localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ''}`, btoa(JSON.stringify(sessionData)));
console.debug('Session data saved');
resolve(true);
}
});
});
}
getSession(slotId: integer): Promise<SessionSaveData> { getSession(slotId: integer): Promise<SessionSaveData> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if (slotId < 0) if (slotId < 0)
@ -578,7 +605,7 @@ export class GameData {
} }
}; };
if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ''}`)) { if (!bypassLogin) {
Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`, true) Utils.apiFetch(`savedata/get?datatype=${GameDataType.SESSION}&slot=${slotId}`, true)
.then(response => response.text()) .then(response => response.text())
.then(async response => { .then(async response => {
@ -591,12 +618,9 @@ export class GameData {
}); });
} else { } else {
const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ''}`); const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ''}`);
if (sessionData) { if (sessionData)
const decFunc = bypassLogin await handleSessionData(atob(sessionData));
? (data: string) => atob(data) else
: (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8);
await handleSessionData(decFunc(sessionData));
} else
return resolve(null); return resolve(null);
} }
}); });
@ -707,7 +731,7 @@ export class GameData {
deleteSession(slotId: integer): Promise<boolean> { deleteSession(slotId: integer): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
if (bypassLogin) { if (bypassLogin) {
localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ''}`); localStorage.removeItem('sessionData');
return resolve(true); return resolve(true);
} }
@ -717,7 +741,6 @@ export class GameData {
Utils.apiFetch(`savedata/delete?datatype=${GameDataType.SESSION}&slot=${slotId}`, true).then(response => { Utils.apiFetch(`savedata/delete?datatype=${GameDataType.SESSION}&slot=${slotId}`, true).then(response => {
if (response.ok) { if (response.ok) {
loggedInUser.lastSessionSlot = -1; loggedInUser.lastSessionSlot = -1;
localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ''}`);
resolve(true); resolve(true);
} }
return response.text(); return response.text();
@ -748,10 +771,8 @@ export class GameData {
return resolve([false, false]); return resolve([false, false]);
const sessionData = this.getSessionSaveData(scene); const sessionData = this.getSessionSaveData(scene);
Utils.apiPost(`savedata/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}`, JSON.stringify(sessionData), undefined, true).then(response => { Utils.apiPost(`savedata/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}`, JSON.stringify(sessionData), undefined, true).then(response => {
if (response.ok) { if (response.ok)
loggedInUser.lastSessionSlot = -1; loggedInUser.lastSessionSlot = -1;
localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ''}`);
}
return response.json(); return response.json();
}).then(jsonResponse => { }).then(jsonResponse => {
if (!jsonResponse.error) if (!jsonResponse.error)
@ -804,13 +825,13 @@ export class GameData {
}) as SessionSaveData; }) as SessionSaveData;
} }
saveAll(scene: BattleScene, skipVerification: boolean = false, sync: boolean = false): Promise<boolean> { saveAll(scene: BattleScene, skipVerification?: boolean): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
Utils.executeIf(!skipVerification, updateUserInfo).then(success => { Utils.executeIf(!skipVerification, updateUserInfo).then(success => {
if (success !== null && !success) if (success !== null && !success)
return resolve(false); return resolve(false);
if (sync) this.scene.ui.savingIcon.show();
this.scene.ui.savingIcon.show(); const data = this.getSystemSaveData();
const sessionData = this.getSessionSaveData(scene); const sessionData = this.getSessionSaveData(scene);
const maxIntAttrValue = Math.pow(2, 31); const maxIntAttrValue = Math.pow(2, 31);
@ -822,12 +843,11 @@ export class GameData {
sessionSlotId: scene.sessionSlotId sessionSlotId: scene.sessionSlotId
}; };
if (!bypassLogin && sync) { if (!bypassLogin) {
Utils.apiPost('savedata/updateall', JSON.stringify(request, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), undefined, true) Utils.apiPost('savedata/updateall', JSON.stringify(request, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), undefined, true)
.then(response => response.text()) .then(response => response.text())
.then(error => { .then(error => {
if (sync) this.scene.ui.savingIcon.hide();
this.scene.ui.savingIcon.hide();
if (error) { if (error) {
if (error.startsWith('client version out of date')) { if (error.startsWith('client version out of date')) {
this.scene.clearPhaseQueue(); this.scene.clearPhaseQueue();
@ -839,18 +859,14 @@ export class GameData {
console.error(error); console.error(error);
return resolve(false); return resolve(false);
} }
localStorage.removeItem('data');
localStorage.removeItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ''}`);
resolve(true); resolve(true);
}); });
} else { } else {
const encFunc = bypassLogin localStorage.setItem('data_bak', localStorage.getItem('data'));
? (data: string) => btoa(data)
: (data: string) => AES.encrypt(data, saveKey);
localStorage.setItem('data', encFunc(JSON.stringify(systemData, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v))); localStorage.setItem('data', btoa(JSON.stringify(systemData, (k: any, v: any) => typeof v === 'bigint' ? v <= maxIntAttrValue ? Number(v) : v.toString() : v)));
localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ''}`, encFunc(JSON.stringify(sessionData))); localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ''}`, btoa(JSON.stringify(sessionData)));
console.debug('Session data saved'); console.debug('Session data saved');
@ -894,12 +910,8 @@ export class GameData {
}); });
} else { } else {
const data = localStorage.getItem(dataKey); const data = localStorage.getItem(dataKey);
if (data) { if (data)
const decFunc = bypassLogin handleData(atob(data));
? (data: string) => atob(data)
: (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8);
handleData(decFunc(data));
}
resolve(!!data); resolve(!!data);
} }
}); });
@ -967,7 +979,7 @@ export class GameData {
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)); 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.showText(`Your ${dataName} data will be overridden and the page will reload. Proceed?`, null, () => {
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
if (!bypassLogin && dataType < GameDataType.SETTINGS && localStorage.getItem(dataKey)) { if (!bypassLogin && dataType < GameDataType.SETTINGS) {
updateUserInfo().then(success => { updateUserInfo().then(success => {
if (!success) if (!success)
return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`); return displayError(`Could not contact the server. Your ${dataName} data could not be imported.`);
@ -978,15 +990,11 @@ export class GameData {
console.error(error); console.error(error);
return displayError(`An error occurred while updating ${dataName} data. Please contact the administrator.`); return displayError(`An error occurred while updating ${dataName} data. Please contact the administrator.`);
} }
localStorage.removeItem(dataKey);
window.location = window.location; window.location = window.location;
}); });
}); });
} else { } else {
const encFunc = bypassLogin || dataType === GameDataType.SETTINGS localStorage.setItem(dataKey, btoa(dataStr));
? (data: string) => btoa(data)
: (data: string) => AES.encrypt(data, saveKey);
localStorage.setItem(dataKey, encFunc(dataStr));
window.location = window.location; window.location = window.location;
} }
}, () => { }, () => {