generated from nhcarrigan/template
feat: add titles system with unlock tracking and character sheet display
Titles are earned by reaching milestones (quests, bosses, gold, clicks, adventurers, guild, prestige, transcendence, apotheosis, achievements, longevity) and are permanent - never lost on prestige/transcendence/ apotheosis resets. 20 titles available at launch. Also fixes a pre-existing P2034 write-conflict on the load backfill path and the exactOptionalPropertyTypes violation in the quest failure handler.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import type { GameState } from "@elysium/types";
|
||||
import { TITLES } from "../data/titles.js";
|
||||
|
||||
export const checkAndUnlockTitles = (
|
||||
currentUnlocked: string[],
|
||||
state: GameState,
|
||||
guildName: string,
|
||||
createdAt: number,
|
||||
): string[] => {
|
||||
const metrics: Record<string, number | boolean> = {
|
||||
totalClicks: state.player.totalClicks,
|
||||
totalGoldEarned: state.player.totalGoldEarned,
|
||||
bossesDefeated: state.bosses.filter((b) => b.status === "defeated").length,
|
||||
questsCompleted: state.quests.filter((q) => q.status === "completed").length,
|
||||
prestigeCount: state.prestige.count,
|
||||
transcendenceCount: state.transcendence?.count ?? 0,
|
||||
apotheosisCount: state.apotheosis?.count ?? 0,
|
||||
adventurerTotal: state.adventurers.reduce((sum, a) => sum + a.count, 0),
|
||||
achievementsUnlocked: state.achievements.filter((a) => a.unlockedAt !== null).length,
|
||||
guildFounded: guildName.trim().length > 0,
|
||||
playedDays: Math.floor((Date.now() - createdAt) / 86_400_000),
|
||||
};
|
||||
|
||||
const newlyUnlocked: string[] = [];
|
||||
|
||||
for (const title of TITLES) {
|
||||
if (currentUnlocked.includes(title.id)) continue;
|
||||
|
||||
const { type, amount } = title.condition;
|
||||
let earned = false;
|
||||
|
||||
if (type === "guildFounded") {
|
||||
earned = metrics.guildFounded === true;
|
||||
} else if (amount !== undefined) {
|
||||
earned = (metrics[type] as number) >= amount;
|
||||
}
|
||||
|
||||
if (earned) {
|
||||
newlyUnlocked.push(title.id);
|
||||
}
|
||||
}
|
||||
|
||||
return newlyUnlocked;
|
||||
};
|
||||
|
||||
export const parseUnlockedTitles = (raw: unknown): string[] => {
|
||||
if (Array.isArray(raw)) {
|
||||
return raw.filter((item): item is string => typeof item === "string");
|
||||
}
|
||||
return [];
|
||||
};
|
||||
Reference in New Issue
Block a user