generated from nhcarrigan/template
fix: unlock exploration areas on zone unlock, correct quest_eternal count, fix quest status filter in timers
This commit is contained in:
@@ -334,8 +334,8 @@ export const defaultAchievements: Array<Achievement> = [
|
||||
unlockedAt: null,
|
||||
},
|
||||
{
|
||||
condition: { amount: 112, type: "questsCompleted" },
|
||||
description: "Complete all 112 quests across the known multiverse.",
|
||||
condition: { amount: 122, type: "questsCompleted" },
|
||||
description: "Complete all 122 quests across the known multiverse.",
|
||||
icon: "🌌",
|
||||
id: "quest_eternal",
|
||||
name: "Quest Eternal",
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import { Hono } from "hono";
|
||||
import { defaultBosses } from "../data/bosses.js";
|
||||
import { defaultEquipmentSets } from "../data/equipmentSets.js";
|
||||
import { defaultExplorations } from "../data/explorations.js";
|
||||
import { prisma } from "../db/client.js";
|
||||
import { authMiddleware } from "../middleware/auth.js";
|
||||
import { updateChallengeProgress } from "../services/dailyChallenges.js";
|
||||
@@ -284,6 +285,19 @@ bossRouter.post("/challenge", async(context) => {
|
||||
continue;
|
||||
}
|
||||
zone.status = "unlocked";
|
||||
|
||||
// Unlock exploration areas for the newly unlocked zone
|
||||
for (const area of state.exploration?.areas ?? []) {
|
||||
const areaDefinition = defaultExplorations.find((explorationArea) => {
|
||||
return explorationArea.id === area.id;
|
||||
});
|
||||
// eslint-disable-next-line capitalized-comments -- v8 ignore
|
||||
/* v8 ignore next 3 -- @preserve */
|
||||
if (areaDefinition?.zoneId === zone.id && area.status === "locked") {
|
||||
area.status = "available";
|
||||
}
|
||||
}
|
||||
|
||||
const updatedZoneBosses = state.bosses.filter((b) => {
|
||||
return b.zoneId === zone.id;
|
||||
});
|
||||
|
||||
@@ -36,10 +36,11 @@ const getQuestTimers = (
|
||||
}> => {
|
||||
return state.quests.
|
||||
filter((quest) => {
|
||||
return quest.status === "in_progress" && quest.startedAt !== undefined;
|
||||
return quest.status === "active" && quest.startedAt !== undefined;
|
||||
}).
|
||||
map((quest) => {
|
||||
const durationMs = quest.durationSeconds * 1000;
|
||||
// eslint-disable-next-line capitalized-comments -- v8 ignore
|
||||
/* v8 ignore next -- @preserve */
|
||||
const endsAt = (quest.startedAt ?? 0) + durationMs;
|
||||
return {
|
||||
@@ -71,6 +72,7 @@ const getExplorationTimers = (
|
||||
return area.status === "in_progress" && area.endsAt !== undefined;
|
||||
}).
|
||||
map((area) => {
|
||||
// eslint-disable-next-line capitalized-comments -- v8 ignore
|
||||
/* v8 ignore next -- @preserve */
|
||||
const endsAt = area.endsAt ?? 0;
|
||||
return {
|
||||
|
||||
@@ -294,6 +294,52 @@ describe("boss route", () => {
|
||||
expect(body.won).toBe(true);
|
||||
});
|
||||
|
||||
it("handles zone unlock gracefully when exploration state is undefined", async () => {
|
||||
const state = makeState({
|
||||
bosses: [makeBoss({ currentHp: 100, maxHp: 100, damagePerSecond: 1 })] as GameState["bosses"],
|
||||
adventurers: [makeAdventurer()] as GameState["adventurers"],
|
||||
zones: [{ id: "test_zone", status: "locked", unlockBossId: "test_boss", unlockQuestId: null }] as GameState["zones"],
|
||||
quests: [],
|
||||
exploration: undefined,
|
||||
});
|
||||
vi.mocked(prisma.gameState.findUnique).mockResolvedValueOnce({ state } as never);
|
||||
vi.mocked(prisma.gameState.update).mockResolvedValueOnce({} as never);
|
||||
const res = await challenge({ bossId: "test_boss" });
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json() as { won: boolean };
|
||||
expect(body.won).toBe(true);
|
||||
});
|
||||
|
||||
it("unlocks exploration areas when a zone is unlocked on boss defeat", async () => {
|
||||
const state = makeState({
|
||||
bosses: [makeBoss({ currentHp: 100, maxHp: 100, damagePerSecond: 1 })] as GameState["bosses"],
|
||||
adventurers: [makeAdventurer()] as GameState["adventurers"],
|
||||
zones: [{ id: "test_zone", status: "locked", unlockBossId: "test_boss", unlockQuestId: null }] as GameState["zones"],
|
||||
quests: [],
|
||||
exploration: {
|
||||
areas: [{ id: "test_area", status: "locked" as const }],
|
||||
materials: [],
|
||||
craftedRecipeIds: [],
|
||||
craftedGoldMultiplier: 1,
|
||||
craftedEssenceMultiplier: 1,
|
||||
craftedClickMultiplier: 1,
|
||||
craftedCombatMultiplier: 1,
|
||||
},
|
||||
});
|
||||
vi.mocked(prisma.gameState.findUnique).mockResolvedValueOnce({ state } as never);
|
||||
let savedState: GameState | undefined;
|
||||
vi.mocked(prisma.gameState.update).mockImplementationOnce(async (args) => {
|
||||
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Test assertion */
|
||||
savedState = (args as { data: { state: GameState } }).data.state;
|
||||
return {} as never;
|
||||
});
|
||||
const res = await challenge({ bossId: "test_boss" });
|
||||
expect(res.status).toBe(200);
|
||||
// Exploration area should remain locked — no matching defaultExploration for "test_area"
|
||||
const area = savedState?.exploration?.areas.find((a) => a.id === "test_area");
|
||||
expect(area?.status).toBe("locked");
|
||||
});
|
||||
|
||||
it("returns 500 when the database throws", async () => {
|
||||
vi.mocked(prisma.gameState.findUnique).mockRejectedValueOnce(new Error("DB error"));
|
||||
const res = await challenge({ bossId: "test_boss" });
|
||||
|
||||
@@ -71,7 +71,7 @@ describe("timers route", () => {
|
||||
{
|
||||
id: "q1",
|
||||
name: "Forest Patrol",
|
||||
status: "in_progress",
|
||||
status: "active",
|
||||
startedAt: startedAt,
|
||||
durationSeconds: 600,
|
||||
},
|
||||
@@ -110,7 +110,7 @@ describe("timers route", () => {
|
||||
{
|
||||
id: "q1",
|
||||
name: "Old Quest",
|
||||
status: "in_progress",
|
||||
status: "active",
|
||||
startedAt: startedAt,
|
||||
durationSeconds: 600,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user