feat: sequential zone unlocking with dual boss+quest gate conditions

Zones now unlock in strict linear order. Each zone requires both the
final boss AND the final quest of the previous zone to be completed:

Verdant Vale → Shattered Ruins (forest_giant + ancient_ruins)
Shattered Ruins → Frozen Peaks (elder_dragon + dragon_lair)
Frozen Peaks → Shadow Marshes (void_titan + storm_citadel)
Shadow Marshes → Volcanic Depths (mud_kraken + plague_ruins)
Volcanic Depths → Astral Void (phoenix_lord + the_forge)

- Zone type gains `unlockQuestId` field alongside `unlockBossId`
- boss.ts checks both conditions before unlocking zone on boss defeat
- tick.ts checks both conditions and unlocks zones + first boss on
  quest completion if the boss condition is already met
- GameContext optimistic update also respects dual-condition logic
- BossPanel zone-gate hints now show both "⚔️ Defeat: X & 📜 Complete: Y"
- game.ts backfill syncs unlockQuestId and re-verifies zone status
  using both conditions, reverting incorrectly-unlocked saves
This commit is contained in:
2026-03-06 16:04:52 -08:00
committed by Naomi Carrigan
parent b9a230f40f
commit e780dc5f6c
7 changed files with 160 additions and 39 deletions
+18 -12
View File
@@ -9,6 +9,7 @@ export const DEFAULT_ZONES: Zone[] = [
emoji: "🌿",
status: "unlocked",
unlockBossId: null,
unlockQuestId: null,
},
{
id: "shattered_ruins",
@@ -17,16 +18,8 @@ export const DEFAULT_ZONES: Zone[] = [
"The remnants of a civilisation long lost to war and dragonfire. Crumbling towers and cursed lakes hide treasures — and an elder dragon who claims these lands as his own.",
emoji: "🏛️",
status: "locked",
unlockBossId: "lich_queen",
},
{
id: "shadow_marshes",
name: "The Shadow Marshes",
description:
"A vast, fog-choked wetland where the sun never fully rises. Dark magic seeps from the earth itself, and things far older than the kingdom lurk beneath the murky waters.",
emoji: "🌑",
status: "locked",
unlockBossId: "troll_king",
unlockBossId: "forest_giant",
unlockQuestId: "ancient_ruins",
},
{
id: "frozen_peaks",
@@ -36,6 +29,17 @@ export const DEFAULT_ZONES: Zone[] = [
emoji: "❄️",
status: "locked",
unlockBossId: "elder_dragon",
unlockQuestId: "dragon_lair",
},
{
id: "shadow_marshes",
name: "The Shadow Marshes",
description:
"A vast, fog-choked wetland where the sun never fully rises. Dark magic seeps from the earth itself, and things far older than the kingdom lurk beneath the murky waters.",
emoji: "🌑",
status: "locked",
unlockBossId: "void_titan",
unlockQuestId: "storm_citadel",
},
{
id: "volcanic_depths",
@@ -44,7 +48,8 @@ export const DEFAULT_ZONES: Zone[] = [
"A chain of active volcanoes whose caverns plunge deep into the earth's molten heart. Legendary forges burn here, tended by fire elementals who serve no master — yet.",
emoji: "🌋",
status: "locked",
unlockBossId: "bone_colossus",
unlockBossId: "mud_kraken",
unlockQuestId: "plague_ruins",
},
{
id: "astral_void",
@@ -53,6 +58,7 @@ export const DEFAULT_ZONES: Zone[] = [
"Beyond the veil of the mortal world lies a realm of pure possibility and absolute terror. Stars are born and die here in moments, and the beings that call this place home have never known mortality.",
emoji: "🌌",
status: "locked",
unlockBossId: "void_titan",
unlockBossId: "phoenix_lord",
unlockQuestId: "the_forge",
},
];