feat: v0.6.0 balance pass, quest-gated bosses, and achievement fixes

This commit is contained in:
2026-07-17 01:02:12 -05:00
parent 9bb1d01d2b
commit 5928eb73d8
49 changed files with 1836 additions and 528 deletions
+3 -3
View File
@@ -8,11 +8,11 @@
type AchievementConditionType =
| "totalGoldEarned"
| "totalClicks"
| "bossesDefeated"
| "questsCompleted"
| "adventurerTotal"
| "prestigeCount"
| "equipmentOwned";
| "equipmentOwned"
| "uniqueQuestsCompleted"
| "uniqueBossesDefeated";
interface AchievementCondition {
type: AchievementConditionType;
+14
View File
@@ -55,6 +55,12 @@ interface Boss {
*/
zoneId: string;
/**
* ID of the quest that must be completed before this boss becomes
* available. `null`/`undefined` means no quest gate applies.
*/
unlockQuestId?: string | null;
/**
* One-time runestone bounty awarded on first-ever defeat.
*/
@@ -66,6 +72,14 @@ interface Boss {
* bounty is never re-awarded in subsequent runs.
*/
bountyRunestonesClaimed?: boolean;
/**
* Whether this boss has ever been defeated, across all prestige/
* transcendence/apotheosis resets. Used by achievements that require
* defeating every distinct boss, since `status` alone only reflects
* the current run.
*/
everDefeated?: boolean;
}
export type { Boss, BossStatus };
+3 -3
View File
@@ -87,7 +87,7 @@ const COMPANIONS: Array<Companion> = [
id: "wren",
name: "Wren",
title: "Hedge Witch",
unlock: { threshold: 500, type: "lifetimeQuests" },
unlock: { threshold: 250, type: "lifetimeQuests" },
},
{
bonus: { type: "bossDamage", value: 0.2 },
@@ -117,7 +117,7 @@ const COMPANIONS: Array<Companion> = [
id: "kael",
name: "Kael",
title: "Battle Mage",
unlock: { threshold: 720, type: "lifetimeBosses" },
unlock: { threshold: 500, type: "lifetimeBosses" },
},
{
bonus: { type: "questTime", value: 0.3 },
@@ -127,7 +127,7 @@ const COMPANIONS: Array<Companion> = [
id: "zuri",
name: "Zuri",
title: "Chrono Weaver",
unlock: { threshold: 950, type: "lifetimeQuests" },
unlock: { threshold: 500, type: "lifetimeQuests" },
},
{
bonus: { type: "passiveGold", value: 0.75 },
@@ -40,6 +40,13 @@ interface Equipment {
*/
owned: boolean;
/**
* Whether the player has ever acquired this item, across all runs — never reset.
* Optional for backwards compatibility with saves persisted before this field
* existed; readers should fall back to `owned` when it is absent.
*/
everOwned?: boolean;
/**
* Whether this item is currently equipped (only one per type can be equipped).
*/
@@ -60,7 +60,7 @@ const DEFAULT_PROFILE_SETTINGS: ProfileSettings = {
enableNotifications: false,
enablePrestigeAnnouncements: true,
enableSounds: false,
numberFormat: "suffix",
numberFormat: "scientific",
showAchievementsUnlocked: true,
showAdventurersRecruited: true,
showApotheosis: true,
+8
View File
@@ -61,6 +61,14 @@ interface Quest {
* Unix timestamp of the most recent failed attempt (if any).
*/
lastFailedAt?: number;
/**
* Whether this quest has ever been completed, across all prestige/
* transcendence/apotheosis resets. Used by achievements that require
* completing every distinct quest, since `status` alone only reflects
* the current run.
*/
everCompleted?: boolean;
}
export type { Quest, QuestReward, QuestRewardType, QuestStatus };
+8 -8
View File
@@ -35,8 +35,8 @@ describe("computeUnlockedCompanionIds", () => {
expect(result).toContain("finn");
});
it("unlocks wren at 500 lifetime quests", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeQuestsCompleted: 500 });
it("unlocks wren at 250 lifetime quests", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeQuestsCompleted: 250 });
expect(result).toContain("finn");
expect(result).toContain("wren");
});
@@ -57,15 +57,15 @@ describe("computeUnlockedCompanionIds", () => {
expect(result).not.toContain("sera");
});
it("unlocks kael at 720 lifetime bosses", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeBossesDefeated: 720 });
it("unlocks kael at 500 lifetime bosses", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeBossesDefeated: 500 });
expect(result).toContain("kael");
expect(result).toContain("lyra");
expect(result).toContain("aldric");
});
it("unlocks zuri at 950 lifetime quests", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeQuestsCompleted: 950 });
it("unlocks zuri at 500 lifetime quests", () => {
const result = computeUnlockedCompanionIds({ ...baseParams, lifetimeQuestsCompleted: 500 });
expect(result).toContain("zuri");
});
@@ -96,8 +96,8 @@ describe("computeUnlockedCompanionIds", () => {
it("unlocks multiple companions simultaneously", () => {
const result = computeUnlockedCompanionIds({
lifetimeBossesDefeated: 720,
lifetimeQuestsCompleted: 950,
lifetimeBossesDefeated: 500,
lifetimeQuestsCompleted: 500,
lifetimeGoldEarned: 1e18,
prestigeCount: 10,
transcendenceCount: 5,
+2 -2
View File
@@ -24,7 +24,7 @@ describe("DEFAULT_PROFILE_SETTINGS", () => {
expect(DEFAULT_PROFILE_SETTINGS.enableNotifications).toBe(false);
});
it("defaults numberFormat to suffix", () => {
expect(DEFAULT_PROFILE_SETTINGS.numberFormat).toBe("suffix");
it("defaults numberFormat to scientific", () => {
expect(DEFAULT_PROFILE_SETTINGS.numberFormat).toBe("scientific");
});
});