generated from nhcarrigan/template
fix: adventurer unlocks not applied by force-unlock tool
The force-unlock debug route now scans completed quests for adventurer rewards and ensures those tiers are marked as unlocked in game state. The UI and API response type both surface the new adventurersUnlocked count alongside existing zone/quest/boss/exploration counts. Closes #88
This commit is contained in:
@@ -257,6 +257,37 @@ const applyBossUnlocks = (state: GameState): number => {
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlocks any adventurer tiers that were granted as rewards for completed quests
|
||||
* but are still locked in the player's state.
|
||||
* @param state - The player's current game state (mutated directly).
|
||||
* @returns The number of adventurer tiers that were unlocked.
|
||||
*/
|
||||
const applyAdventurerUnlocks = (state: GameState): number => {
|
||||
let count = 0;
|
||||
const earnedAdventurerIds = new Set<string>();
|
||||
|
||||
for (const quest of state.quests) {
|
||||
if (quest.status !== "completed") {
|
||||
continue;
|
||||
}
|
||||
for (const reward of quest.rewards) {
|
||||
if (reward.type === "adventurer" && reward.targetId !== undefined) {
|
||||
earnedAdventurerIds.add(reward.targetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const adventurer of state.adventurers) {
|
||||
if (!adventurer.unlocked && earnedAdventurerIds.has(adventurer.id)) {
|
||||
adventurer.unlocked = true;
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes available any exploration areas whose parent zone is now unlocked.
|
||||
* @param state - The player's current game state (mutated directly).
|
||||
@@ -301,6 +332,7 @@ const applyExplorationUnlocks = (state: GameState): number => {
|
||||
const applyForceUnlocks = (
|
||||
state: GameState,
|
||||
): {
|
||||
adventurersUnlocked: number;
|
||||
bossesUnlocked: number;
|
||||
explorationUnlocked: number;
|
||||
questsUnlocked: number;
|
||||
@@ -310,7 +342,14 @@ const applyForceUnlocks = (
|
||||
const questsUnlocked = applyQuestUnlocks(state);
|
||||
const bossesUnlocked = applyBossUnlocks(state);
|
||||
const explorationUnlocked = applyExplorationUnlocks(state);
|
||||
return { bossesUnlocked, explorationUnlocked, questsUnlocked, zonesUnlocked };
|
||||
const adventurersUnlocked = applyAdventurerUnlocks(state);
|
||||
return {
|
||||
adventurersUnlocked,
|
||||
bossesUnlocked,
|
||||
explorationUnlocked,
|
||||
questsUnlocked,
|
||||
zonesUnlocked,
|
||||
};
|
||||
};
|
||||
|
||||
const debugRouter = new Hono<HonoEnvironment>();
|
||||
@@ -330,8 +369,13 @@ debugRouter.post("/force-unlocks", async(context) => {
|
||||
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Prisma stores state as JSON object */
|
||||
const state = gameStateRecord.state as unknown as GameState;
|
||||
|
||||
const { bossesUnlocked, explorationUnlocked, questsUnlocked, zonesUnlocked }
|
||||
= applyForceUnlocks(state);
|
||||
const {
|
||||
adventurersUnlocked,
|
||||
bossesUnlocked,
|
||||
explorationUnlocked,
|
||||
questsUnlocked,
|
||||
zonesUnlocked,
|
||||
} = applyForceUnlocks(state);
|
||||
|
||||
const updatedAt = Date.now();
|
||||
await prisma.gameState.update({
|
||||
@@ -347,6 +391,7 @@ debugRouter.post("/force-unlocks", async(context) => {
|
||||
: computeHmac(JSON.stringify(state), secret);
|
||||
|
||||
return context.json({
|
||||
adventurersUnlocked,
|
||||
bossesUnlocked,
|
||||
explorationUnlocked,
|
||||
questsUnlocked,
|
||||
|
||||
@@ -51,11 +51,15 @@ const DebugPanel = (): JSX.Element => {
|
||||
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.explorationUnlocked
|
||||
+ result.adventurersUnlocked;
|
||||
const message
|
||||
= parts.length === 0
|
||||
? "Everything looks correct — no missing unlocks were found."
|
||||
|
||||
@@ -558,6 +558,7 @@ interface GameContextValue {
|
||||
* @returns Counts of what was corrected.
|
||||
*/
|
||||
forceUnlocks: ()=> Promise<{
|
||||
adventurersUnlocked: number;
|
||||
bossesUnlocked: number;
|
||||
explorationUnlocked: number;
|
||||
questsUnlocked: number;
|
||||
@@ -2104,6 +2105,7 @@ export const GameProvider = ({
|
||||
localStorage.setItem("elysium_save_signature", data.signature);
|
||||
}
|
||||
return {
|
||||
adventurersUnlocked: data.adventurersUnlocked,
|
||||
bossesUnlocked: data.bossesUnlocked,
|
||||
explorationUnlocked: data.explorationUnlocked,
|
||||
questsUnlocked: data.questsUnlocked,
|
||||
@@ -2116,6 +2118,7 @@ export const GameProvider = ({
|
||||
: "Failed to force unlocks",
|
||||
);
|
||||
return {
|
||||
adventurersUnlocked: 0,
|
||||
bossesUnlocked: 0,
|
||||
explorationUnlocked: 0,
|
||||
questsUnlocked: 0,
|
||||
|
||||
@@ -425,6 +425,11 @@ interface ForceUnlocksResponse {
|
||||
*/
|
||||
explorationUnlocked: number;
|
||||
|
||||
/**
|
||||
* Number of adventurer tiers that were unlocked by this operation.
|
||||
*/
|
||||
adventurersUnlocked: number;
|
||||
|
||||
/**
|
||||
* HMAC-SHA256 signature of the corrected state for anti-cheat chain continuity.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user