fix: extend force-unlock to cover upgrades, equipment, and story chapters
CI / Lint, Build & Test (pull_request) Failing after 1m8s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m10s

The force-unlock debug tool now also corrects three additional categories
of stuck state:

- Upgrades: scans defeated bosses and completed quests for upgrade
  rewards and marks those upgrades as unlocked
- Equipment: scans defeated bosses for equipment rewards and marks those
  items as owned (without auto-equipping)
- Story chapters: checks every chapter's unlock condition against the
  current game state and adds any missing IDs to unlockedChapterIds

The result message in the debug panel now reports all eight corrected
categories.
This commit is contained in:
2026-03-20 10:00:08 -07:00
committed by Naomi Carrigan
parent 715ccd3fc7
commit 16c95f4fb3
4 changed files with 191 additions and 28 deletions
+44 -27
View File
@@ -12,6 +12,49 @@ import { ConfirmationModal } from "../ui/confirmationModal.js";
type ActiveModal = "force-unlocks" | "hard-reset" | null;
interface ForceUnlocksResult {
adventurersUnlocked: number;
bossesUnlocked: number;
equipmentUnlocked: number;
explorationUnlocked: number;
questsUnlocked: number;
storyUnlocked: number;
upgradesUnlocked: number;
zonesUnlocked: number;
}
/**
* Builds a human-readable summary of what the force-unlock operation corrected.
* @param result - The counts returned by the force-unlock operation.
* @returns A message string describing what was fixed, or a confirmation that nothing needed fixing.
*/
const buildForceUnlocksMessage = (result: ForceUnlocksResult): string => {
const entries: Array<[ number, string ]> = [
[ result.zonesUnlocked, "zone(s)" ],
[ result.questsUnlocked, "quest(s)" ],
[ result.bossesUnlocked, "boss(es)" ],
[ result.explorationUnlocked, "exploration area(s)" ],
[ result.adventurersUnlocked, "adventurer tier(s)" ],
[ result.upgradesUnlocked, "upgrade(s)" ],
[ result.equipmentUnlocked, "equipment item(s)" ],
[ result.storyUnlocked, "story chapter(s)" ],
];
const parts = entries.
filter(([ count ]) => {
return count > 0;
}).
map(([ count, label ]) => {
return `${String(count)} ${label}`;
});
if (parts.length === 0) {
return "Everything looks correct — no missing unlocks were found.";
}
const total = entries.reduce((sum, [ count ]) => {
return sum + count;
}, 0);
return `Fixed ${String(total)} unlock(s): ${parts.join(", ")}.`;
};
/**
* Renders the debug panel with tools for fixing stuck game state.
* @returns The JSX element.
@@ -38,33 +81,7 @@ const DebugPanel = (): JSX.Element => {
setActiveModal(null);
void (async(): Promise<void> => {
const result = await forceUnlocks();
const parts: Array<string> = [];
if (result.zonesUnlocked > 0) {
parts.push(`${String(result.zonesUnlocked)} zone(s)`);
}
if (result.questsUnlocked > 0) {
parts.push(`${String(result.questsUnlocked)} quest(s)`);
}
if (result.bossesUnlocked > 0) {
parts.push(`${String(result.bossesUnlocked)} boss(es)`);
}
if (result.explorationUnlocked > 0) {
parts.push(`${String(result.explorationUnlocked)} exploration area(s)`);
}
if (result.adventurersUnlocked > 0) {
parts.push(`${String(result.adventurersUnlocked)} adventurer tier(s)`);
}
const total
= result.zonesUnlocked
+ result.questsUnlocked
+ result.bossesUnlocked
+ result.explorationUnlocked
+ result.adventurersUnlocked;
const message
= parts.length === 0
? "Everything looks correct — no missing unlocks were found."
: `Fixed ${String(total)} unlock(s): ${parts.join(", ")}.`;
setForceUnlocksResult(message);
setForceUnlocksResult(buildForceUnlocksMessage(result));
})();
}
+9
View File
@@ -560,8 +560,11 @@ interface GameContextValue {
forceUnlocks: ()=> Promise<{
adventurersUnlocked: number;
bossesUnlocked: number;
equipmentUnlocked: number;
explorationUnlocked: number;
questsUnlocked: number;
storyUnlocked: number;
upgradesUnlocked: number;
zonesUnlocked: number;
}>;
@@ -2107,8 +2110,11 @@ export const GameProvider = ({
return {
adventurersUnlocked: data.adventurersUnlocked,
bossesUnlocked: data.bossesUnlocked,
equipmentUnlocked: data.equipmentUnlocked,
explorationUnlocked: data.explorationUnlocked,
questsUnlocked: data.questsUnlocked,
storyUnlocked: data.storyUnlocked,
upgradesUnlocked: data.upgradesUnlocked,
zonesUnlocked: data.zonesUnlocked,
};
} catch (error_: unknown) {
@@ -2120,8 +2126,11 @@ export const GameProvider = ({
return {
adventurersUnlocked: 0,
bossesUnlocked: 0,
equipmentUnlocked: 0,
explorationUnlocked: 0,
questsUnlocked: 0,
storyUnlocked: 0,
upgradesUnlocked: 0,
zonesUnlocked: 0,
};
}