Skip to content

Commit 833ac0c

Browse files
SirzBenjieDayKevdamocleasfabske0Bertie690
authored
feat!: Hotfix 1.12.0.1 (#7426)
* fix: Starmie no longer has the wrong attack stat No idea how this got through, some pr probably ignored that we had it at 100 and it was missed in a lot of other changes. * chore!: update version to `1.12.0.1` * Balance Fixes, Update Submodules - Battle Bond Greninja is now set as a Sub-Legendary to prevent it from generating too early and other effects - Fixed Mega Delphox passive * fix: fix pokedex console spam (#7422) * fix!: block saving if dex data doesn't match starter data (#7420) * misc: block saving if dex data doesn't match starter data * Use `for...of` instead of `for...in` * Add migrator to unlock starters that are missing dex data * Remove classic win count from hasStarterData validation * Remove classic win count check in buggy migrator data. --------- Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * test: fix parameter types/doc comments of matchers (#7424) * misc: add alert screen when rejecting saving due to invalid save #7428 * Add alert screen when rejecting saving due to invalid save * Remove obsolete `savingIcon.hide()` call * Update locales * Remove testing log --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * fix(beta): make text box for new save reload alert wider * extra day for event * dev: disable debug log in `species-data-registry.ts` due to pokedex --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: damocleas <damocleas25@gmail.com> Co-authored-by: Fabi <192151969+fabske0@users.noreply.github.com> Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
1 parent c813593 commit 833ac0c

16 files changed

Lines changed: 225 additions & 21 deletions

File tree

locales

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "pokemon-rogue-battle",
33
"private": true,
4-
"version": "1.12.0.0",
4+
"version": "1.12.0.1",
55
"type": "module",
66
"scripts": {
77
"start:prod": "vite --mode production",

src/data/balance/species/generation-01.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12049,7 +12049,7 @@ export function initGenerationOne(): SpeciesDataMapConfig {
1204912049
abilityHidden: AbilityId.HUGE_POWER,
1205012050
baseTotal: 660,
1205112051
baseHp: 60,
12052-
baseAtk: 140,
12052+
baseAtk: 100,
1205312053
baseDef: 105,
1205412054
baseSpatk: 130,
1205512055
baseSpdef: 105,

src/data/balance/species/generation-06.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ export function initGenerationSix(): SpeciesDataMapConfig {
600600
],
601601
passives: {
602602
0: AbilityId.NEUROFORCE,
603-
1: AbilityId.NEUROFORCE,
603+
1: AbilityId.MAGICIAN,
604604
},
605605
levelMoves: [
606606
[RELEARN_MOVE, MoveId.SHADOW_BALL],

src/data/balance/species/generation-07.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12009,6 +12009,7 @@ export function initGenerationSeven(): SpeciesDataMapConfig {
1200912009
species: new PokemonSpecies({
1201012010
id: SpeciesId.BATTLE_BOND_GRENINJA,
1201112011
generation: 7,
12012+
subLegendary: true,
1201212013
category: "Ninja Pokémon",
1201312014
type1: PokemonType.WATER,
1201412015
type2: PokemonType.DARK,

src/data/balance/timed-events.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const timedEvents: readonly TimedEvent[] = [
1212
name: "Mega Pride 2026",
1313
eventType: EventType.SHINY,
1414
startDate: new Date(Date.UTC(2026, 5, 23)),
15-
endDate: new Date(Date.UTC(2026, 6, 13)),
15+
endDate: new Date(Date.UTC(2026, 6, 14)),
1616
bannerKey: "pride2026",
1717
scale: 0.21,
1818
availableLangs: [

src/data/species-data-registry.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export class SpeciesDataRegistry {
155155
const tms = [...speciesData.tms, ...(speciesData.formTms?.[formKey] ?? [])];
156156
const prevo = this.getPrevolution(speciesId);
157157
if (prevo !== null) {
158-
const prevoTms = this.getTms(prevo, formKey);
158+
const prevoTms = this.getTms(prevo, form ? formKey : undefined);
159159
tms.push(...prevoTms);
160160
}
161161

@@ -438,7 +438,6 @@ export class SpeciesDataRegistry {
438438
}
439439

440440
if (form == null) {
441-
console.debug(`No form requested for ${speciesId} (${this.getSpecies(speciesId).name}), returning base form.`);
442441
return forms[0].formKey;
443442
}
444443

src/enums/ui-mode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ export enum UiMode {
4747
ADMIN,
4848
MYSTERY_ENCOUNTER,
4949
CHANGE_PASSWORD_FORM,
50+
ALERT_MODAL,
5051
}

src/system/game-data.ts

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler";
7474
import { applyChallenges } from "#utils/challenge-utils";
7575
import { fixedInt, NumberHolder, randInt, randSeedItem } from "#utils/common";
7676
import { decrypt, encrypt } from "#utils/data";
77-
import { getEnumKeys } from "#utils/enums";
77+
import { getEnumKeys, getEnumValues } from "#utils/enums";
7878
import { getPokemonSpecies } from "#utils/pokemon-utils";
7979
import { toCamelCase } from "#utils/strings";
8080
import { AES, enc } from "crypto-js";
@@ -214,10 +214,81 @@ export class GameData {
214214
return this.unlocks[unlockable];
215215
}
216216

217+
/**
218+
* @returns Whether the system data is valid
219+
*/
220+
private validateSystemData(data: SystemSaveData): boolean {
221+
if (data.starterData == null) {
222+
console.error("Starter data missing!");
223+
return false;
224+
}
225+
226+
for (const speciesId of getEnumValues(SpeciesId)) {
227+
if (!speciesDataRegistry.isStarter(speciesId) || defaultStarterSpecies.includes(speciesId)) {
228+
continue;
229+
}
230+
231+
const starterEntry = data.starterData[speciesId];
232+
const dexEntry = data.dexData[speciesId];
233+
234+
const species = SpeciesId[speciesId];
235+
236+
if (starterEntry == null) {
237+
console.error("Missing starter data for %s (%d)!", species, speciesId);
238+
return false;
239+
}
240+
if (dexEntry == null) {
241+
console.error("Missing dex data for %s (%d)!", species, speciesId);
242+
return false;
243+
}
244+
245+
const hasStarterData =
246+
starterEntry.abilityAttr > 1
247+
|| starterEntry.eggMoves > 0
248+
|| starterEntry.moveset != null
249+
|| starterEntry.passiveAttr > 0
250+
|| starterEntry.valueReduction > 0;
251+
252+
const noDexData = dexEntry.caughtCount === 0 && dexEntry.hatchedCount === 0 && dexEntry.caughtAttr === 0n;
253+
254+
if (hasStarterData && noDexData) {
255+
console.error("Corrupt save data detected, save rejected!");
256+
console.warn("Species: %s (%d)", species, speciesId);
257+
console.warn(starterEntry);
258+
console.warn(dexEntry);
259+
return false;
260+
}
261+
}
262+
263+
return true;
264+
}
265+
266+
private async showInvalidSaveModal<const T>(returnValue: T): Promise<T> {
267+
const { promise, resolve } = Promise.withResolvers<T>();
268+
await globalScene.ui.setMode(UiMode.ALERT_MODAL, i18next.t("gameData:failedSaveValidation"));
269+
// TODO: This is a temporary hacky solution to ensure the modal displays when saving
270+
// on the starter select UI, which change the UI mode without awaiting this async call..
271+
globalScene.time.delayedCall(fixedInt(1000), () => {
272+
// on the pokedex page, which changes the UiMode after calling this so the
273+
// user never sees the alert modal.
274+
if (globalScene.ui.getMode() === UiMode.ALERT_MODAL) {
275+
globalScene.time.delayedCall(fixedInt(4000), () => resolve(returnValue));
276+
} else {
277+
globalScene.ui.setMode(UiMode.ALERT_MODAL, i18next.t("gameData:failedSaveValidation"));
278+
globalScene.time.delayedCall(fixedInt(4000), () => resolve(returnValue));
279+
}
280+
});
281+
return promise;
282+
}
283+
217284
public async saveSystem(): Promise<boolean> {
218-
globalScene.ui.savingIcon.show();
219285
const data = this.getSystemSaveData();
220286

287+
if (!this.validateSystemData(data)) {
288+
return this.showInvalidSaveModal(false);
289+
}
290+
globalScene.ui.savingIcon.show();
291+
221292
const maxIntAttrValue = 0x80000000;
222293
const systemData = JSON.stringify(data, (_k: any, v: any) =>
223294
typeof v === "bigint" ? (v <= maxIntAttrValue ? Number(v) : v.toString()) : v,
@@ -1240,10 +1311,6 @@ export class GameData {
12401311
}
12411312
}
12421313

1243-
if (sync) {
1244-
globalScene.ui.savingIcon.show();
1245-
}
1246-
12471314
const sessionData = useCachedSession
12481315
? this.parseSessionData(
12491316
decrypt(localStorage.getItem(getSessionDataLocalStorageKey(globalScene.sessionSlotId))!, bypassLogin),
@@ -1256,6 +1323,15 @@ export class GameData {
12561323
? GameData.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin))
12571324
: this.getSystemSaveData(); // TODO: is this bang correct?
12581325

1326+
if (!this.validateSystemData(systemData)) {
1327+
return this.showInvalidSaveModal(false);
1328+
}
1329+
1330+
// Saving icon should go after validation to avoid confusing users.
1331+
if (sync) {
1332+
globalScene.ui.savingIcon.show();
1333+
}
1334+
12591335
const request = {
12601336
system: systemData,
12611337
session: sessionData,

0 commit comments

Comments
 (0)