{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-9950-7dd2-8f8f-d7b5ee158437","file":"TjGFcoMQ.mjs","mappings":"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","names":["CATEGORY_ORDER","ACHIEVEMENTS","msg","ACHIEVEMENTS_BY_ID","a"],"sources":["../../../../../../src/lib/achievements/catalog.ts"],"sourcesContent":["// Achievement catalog — the single source of truth, used by both the server\n// (evaluation/granting) and the client (display). Pure data, so it's\n// safe in either context (msg() only marks text for translation, RAT-573). Add\n// an achievement by appending an entry; the medal art is the `glyph` key\n// (rendered by AchievementMedal) + `tier` colour.\n\nimport { msg } from '$lib/i18n/mark';\n\nexport type AchievementTier = 'bronze' | 'silver' | 'gold' | 'mythic';\nexport type AchievementCategory =\n\t| 'tenure'\n\t| 'activity'\n\t| 'combat'\n\t| 'shame'\n\t| 'social'\n\t| 'dedication'\n\t| 'event'\n\t| 'secret';\n\n/** The user-stat each achievement is measured against. Computed server-side in\n *  $lib/server/achievements. */\nexport type AchievementMetric =\n\t| 'messages' // chat + group + duel comments sent\n\t| 'rates' // chat rates handed out (raters)\n\t| 'duels_created'\n\t| 'duel_wins'\n\t| 'daily_streak'\n\t| 'followers'\n\t| 'referrals'\n\t| 'arena_votes'\n\t| 'tenure_days'\n\t| 'signup_ordinal' // 1-based join rank (earlier = lower)\n\t| 'tokens_spent'\n\t| 'duel_losses'\n\t| 'proportions_min' // lowest AI \"Proportions\" score across all rated photos; 0 = no data\n\t| 'peer_length_avg'; // avg score_length received over >=3 rated peer rates; 0 = not enough data\n\nexport interface AchievementDef {\n\tid: string;\n\ttitle: string;\n\tdesc: string;\n\tcategory: AchievementCategory;\n\ttier: AchievementTier;\n\t/** glyph key — see AchievementMedal's GLYPHS map. */\n\tglyph: string;\n\t/** the user-stat this is measured against. Omitted for `manual` ones. */\n\tmetric?: AchievementMetric;\n\t/** earn when metric >= threshold (default), or <= for `compare:'lte'`. */\n\tthreshold?: number;\n\tcompare?: 'gte' | 'lte';\n\t/** prestige — also rides next to the username. */\n\tbadge?: boolean;\n\t/** only meaningful for raters; hidden from non-rater catalogs. */\n\traterOnly?: boolean;\n\t/** granted by code/event, never by metric evaluation (skipped by the engine). */\n\tmanual?: boolean;\n\t/** hidden from the grid until earned — discovered, not advertised. */\n\tsecret?: boolean;\n}\n\nexport const CATEGORY_ORDER: AchievementCategory[] = [\n\t'secret',\n\t'tenure',\n\t'activity',\n\t'combat',\n\t'shame',\n\t'social',\n\t'dedication',\n\t'event'\n];\n\nexport const ACHIEVEMENTS: AchievementDef[] = [\n\t// ── tenure ──\n\t{ id: 'founder', title: msg('founder'), desc: msg('here before it made sense. account from the first wave.'), category: 'tenure', tier: 'gold', glyph: 'shield', metric: 'signup_ordinal', threshold: 500, compare: 'lte', badge: true },\n\t{ id: 'one_year_in', title: msg('one year in'), desc: msg('a full year of questionable decisions. happy jubilee.'), category: 'tenure', tier: 'gold', glyph: 'cake', metric: 'tenure_days', threshold: 365, badge: true },\n\t{ id: 'pioneer', title: msg('pioneer'), desc: msg('joined before the 10,000th user. you were early.'), category: 'tenure', tier: 'silver', glyph: 'flag', metric: 'signup_ordinal', threshold: 10000, compare: 'lte' },\n\n\t// ── activity ──\n\t{ id: 'chatter_gold', title: msg('menace to the chat'), desc: msg('10,000 messages sent. log off occasionally.'), category: 'activity', tier: 'gold', glyph: 'chat', metric: 'messages', threshold: 10000 },\n\t{ id: 'chatter_silver', title: msg('talker'), desc: msg('1,000 messages sent.'), category: 'activity', tier: 'silver', glyph: 'chat', metric: 'messages', threshold: 1000 },\n\t{ id: 'chatter_bronze', title: msg('warming up'), desc: msg('100 messages sent.'), category: 'activity', tier: 'bronze', glyph: 'chat', metric: 'messages', threshold: 100 },\n\t{ id: 'arena_judge', title: msg('arena judge'), desc: msg('cast 500 arena votes.'), category: 'activity', tier: 'silver', glyph: 'gavel', metric: 'arena_votes', threshold: 500 },\n\n\t// ── combat ──\n\t{ id: 'first_blood', title: msg('first blood'), desc: msg('created your first duel.'), category: 'combat', tier: 'bronze', glyph: 'sword', metric: 'duels_created', threshold: 1 },\n\t{ id: 'duel_champion', title: msg('duel champion'), desc: msg('won 10 duels. the AI respects you. nobody else does.'), category: 'combat', tier: 'gold', glyph: 'crown', metric: 'duel_wins', threshold: 10, badge: true },\n\t{ id: 'trusted_rater', title: msg('trusted rater'), desc: msg('1,000 rates handed out via chat.'), category: 'combat', tier: 'silver', glyph: 'star', metric: 'rates', threshold: 1000, raterOnly: true },\n\t{ id: 'cannon_fodder', title: msg('cannon fodder'), desc: msg('lost 10 duels. somebody has to feed the winners.'), category: 'combat', tier: 'bronze', glyph: 'skull', metric: 'duel_losses', threshold: 10 },\n\t{ id: 'punching_bag', title: msg('punching bag'), desc: msg('lost 25 duels. at this point it\\'s a personality.'), category: 'combat', tier: 'silver', glyph: 'skull', metric: 'duel_losses', threshold: 25 },\n\n\t// ── shame — worn proudly ──\n\t{ id: 'fun_sized', title: msg('fun sized'), desc: msg('the AI ran the numbers on proportions. compact. it happens.'), category: 'shame', tier: 'bronze', glyph: 'ruler', metric: 'proportions_min', threshold: 4, compare: 'lte' },\n\t{ id: 'certified_micro', title: msg('certified micro'), desc: msg('proportions scored 2 or under. certified. own it.'), category: 'shame', tier: 'gold', glyph: 'lens', metric: 'proportions_min', threshold: 2, compare: 'lte' },\n\t{ id: 'jury_has_spoken', title: msg('the jury has spoken'), desc: msg('three or more humans rated the length. the verdict wasn\\'t kind.'), category: 'shame', tier: 'silver', glyph: 'scales', metric: 'peer_length_avg', threshold: 4, compare: 'lte' },\n\n\t// ── social ──\n\t{ id: 'somebody', title: msg('somebody'), desc: msg('100 followers.'), category: 'social', tier: 'silver', glyph: 'users', metric: 'followers', threshold: 100 },\n\t{ id: 'recruiter', title: msg('recruiter'), desc: msg('brought 5 friends down with you.'), category: 'social', tier: 'bronze', glyph: 'userplus', metric: 'referrals', threshold: 5 },\n\n\t// ── dedication ──\n\t{ id: 'unbreakable', title: msg('unbreakable'), desc: msg('100-day daily streak. seek help.'), category: 'dedication', tier: 'gold', glyph: 'flame', metric: 'daily_streak', threshold: 100 },\n\t{ id: 'whale', title: msg('whale'), desc: msg('spent a frankly concerning number of tokens.'), category: 'dedication', tier: 'gold', glyph: 'gem', metric: 'tokens_spent', threshold: 1000 },\n\n\t// ── event: truth or dare week (2026-09) ──\n\t// Code-granted from the match engine (src/lib/server/tod.ts endMatch); the\n\t// week board's top 10 is granted by hand when the week closes.\n\t{ id: 'tod_first_dare', title: msg('first dare'), desc: msg('the bottle landed, you delivered. no going back.'), category: 'event', tier: 'bronze', glyph: 'flame', manual: true },\n\t{ id: 'tod_no_chicken', title: msg('no chicken'), desc: msg('a full truth or dare table without backing out once.'), category: 'event', tier: 'silver', glyph: 'shield', manual: true },\n\t{ id: 'tod_glass', title: msg('glass'), desc: msg('the glass shot. pressed flat, shot from the other side.'), category: 'event', tier: 'silver', glyph: 'lens', manual: true },\n\t{ id: 'tod_host', title: msg('host with the most'), desc: msg('three truth or dare tables filled to eight.'), category: 'event', tier: 'gold', glyph: 'crown', manual: true },\n\t{ id: 'tod_top10', title: msg('truth or dare top 10'), desc: msg('week board finish. the bottle knew.'), category: 'event', tier: 'gold', glyph: 'star', manual: true, badge: true },\n\n\t// ── conquest (the 3-player map quiz) ──\n\t// Code-granted at the end of a match (src/lib/server/conquest/engine.ts finishMatch).\n\t{ id: 'conquest_first_win', title: msg('first conquest'), desc: msg('won a conquest match. pornland bows.'), category: 'event', tier: 'bronze', glyph: 'flag', manual: true },\n\t{ id: 'conquest_castle_breaker', title: msg('castle breaker'), desc: msg('took a castle down and everything it owned.'), category: 'event', tier: 'silver', glyph: 'sword', manual: true },\n\t{ id: 'conquest_emperor', title: msg('emperor of pornland'), desc: msg('ended a match holding 20+ tiles.'), category: 'event', tier: 'gold', glyph: 'crown', manual: true },\n\n\t// ── strip poker ──\n\t// Code-granted when a table ends (src/lib/server/poker/engine.ts finishTable).\n\t{ id: 'poker_still_dressed', title: msg('still dressed'), desc: msg('last one with clothes on at a strip poker table.'), category: 'event', tier: 'bronze', glyph: 'shield', manual: true },\n\t{ id: 'poker_face', title: msg('poker face'), desc: msg('raised with nothing and watched them all fold.'), category: 'event', tier: 'silver', glyph: 'flame', manual: true },\n\t{ id: 'poker_monster', title: msg('monster hand'), desc: msg('won a showdown with four of a kind or better.'), category: 'event', tier: 'gold', glyph: 'gem', manual: true },\n\n\t// ── secret ──\n\t// One-time, code-unlocked. The puzzle lives outside the app (a message users\n\t// decode); the answer is MYTHOS_CODE (env, never in the repo). Hidden from the\n\t// grid until earned — then it shows in full holographic glory.\n\t{ id: 'mythos', title: msg('mythos'), desc: msg('you decoded the unspoken. one of the few who ever will.'), category: 'secret', tier: 'mythic', glyph: 'mythos', manual: true, secret: true, badge: true }\n];\n\nexport const ACHIEVEMENTS_BY_ID: Record<string, AchievementDef> = Object.fromEntries(\n\tACHIEVEMENTS.map((a) => [a.id, a])\n);\n\n/** Earned check for a metric value. Manual achievements (no threshold) never\n *  earn this way — they're granted by code/event. */\nexport function isEarned(def: AchievementDef, value: number): boolean {\n\tif (def.threshold == null) return false;\n\treturn def.compare === 'lte' ? value <= def.threshold && value > 0 : value >= def.threshold;\n}\n"],"version":3}