// Data: badges, categories, mock ranking, mock history seeds. const CATEGORIES = [ { id: 'work', label: 'Trabalho', ico: 'briefcase', color: '#6ad8ff' }, { id: 'study', label: 'Estudo', ico: 'book', color: '#b388ff' }, { id: 'health', label: 'Saúde', ico: 'heart', color: '#58e0a0' }, { id: 'mind', label: 'Mente', ico: 'brain', color: '#f4c542' }, { id: 'side', label: 'Projeto', ico: 'spark', color: '#ff8a4a' }, ]; const DIFFICULTIES = [ { id: 'common', label: 'Trivial', xp: 50, rarity: 'common' }, { id: 'rare', label: 'Padrão', xp: 150, rarity: 'rare' }, { id: 'epic', label: 'Desafiadora', xp: 350, rarity: 'epic' }, { id: 'legendary', label: 'Lendária', xp: 700, rarity: 'legendary' }, ]; // Level system: tier breakpoints const TIERS = [ { name: 'Iniciante', min: 0, max: 500 }, { name: 'Adepto', min: 500, max: 1500 }, { name: 'Veterano', min: 1500, max: 3000 }, { name: 'Mestre', min: 3000, max: 6000 }, { name: 'Lendário', min: 6000, max: Infinity }, ]; // XP needed for level N -> N+1 (progressive) const xpForLevel = (lvl) => Math.round(200 + (lvl - 1) * 80 + Math.pow(lvl, 1.6) * 12); const computeLevel = (totalXp) => { let lvl = 1, accum = 0; while (true) { const need = xpForLevel(lvl); if (accum + need > totalXp) return { level: lvl, intoLevel: totalXp - accum, needLevel: need, totalAtLevel: accum }; accum += need; lvl += 1; if (lvl > 99) return { level: 99, intoLevel: 0, needLevel: 1, totalAtLevel: accum }; } }; const tierFor = (totalXp) => { for (let i = TIERS.length - 1; i >= 0; i--) if (totalXp >= TIERS[i].min) return { ...TIERS[i], idx: i }; return { ...TIERS[0], idx: 0 }; }; const BADGES = [ { id: 'first_blood', name: 'Primeira lâmina', req: 'Conclua 1 missão', cond: (s) => s.totalCompleted >= 1, progress: (s) => s.totalCompleted / 1, glyph: 'I' }, { id: 'three_firm', name: 'Três dias firmes', req: 'Mantenha streak de 3 dias', cond: (s) => s.streak >= 3, progress: (s) => s.streak / 3, glyph: 'III' }, { id: 'week_shield', name: 'Semana blindada', req: 'Mantenha streak de 7 dias', cond: (s) => s.streak >= 7, progress: (s) => s.streak / 7, glyph: 'VII' }, { id: 'executor', name: 'Executor', req: 'Conclua 5 missões', cond: (s) => s.totalCompleted >= 5, progress: (s) => s.totalCompleted / 5, glyph: 'V' }, { id: 'beginner', name: 'Iniciante focado', req: 'Acumule 500 XP', cond: (s) => s.xp >= 500, progress: (s) => s.xp / 500, glyph: 'φ' }, { id: 'rhythm', name: 'Ritmo de campanha',req: 'Acumule 1500 XP', cond: (s) => s.xp >= 1500, progress: (s) => s.xp / 1500, glyph: '∞' }, { id: 'epic_hunter', name: 'Caçador épico', req: 'Conclua 1 missão Épica', cond: (s) => s.completedByRarity?.epic >= 1, progress: (s) => (s.completedByRarity?.epic || 0) / 1, glyph: '◆' }, { id: 'legendary', name: 'Caçador lendário', req: 'Conclua 1 missão Lendária', cond: (s) => s.completedByRarity?.legendary >= 1, progress: (s) => (s.completedByRarity?.legendary || 0) / 1, glyph: '★' }, ]; const RANKING = [ { name: 'Aldebrã, o Implacável', title: 'Mestre', xp: 5840, you: false }, { name: 'Iara da Tempestade', title: 'Veterano', xp: 4210, you: false }, { name: 'Rui Punho-de-Aço', title: 'Veterano', xp: 3380, you: false }, // YOU is injected { name: 'Lince Noturno', title: 'Adepto', xp: 1090, you: false }, { name: 'Murmúrio', title: 'Adepto', xp: 880, you: false }, { name: 'Vyra dos Brumas', title: 'Adepto', xp: 620, you: false }, { name: 'Cardus, o Calmo', title: 'Iniciante', xp: 280, you: false }, ]; // Initial mock state — gives the prototype something to play with on load const SEED_QUESTS = [ { id: 'q1', title: 'Revisar e fechar 30 questões do simulado', desc: 'Pomodoro 4×25min — bater meta semanal de revisão', category: 'study', rarity: 'rare', xp: 150, status: 'open', deadline: 'Hoje', created: Date.now() - 1000*60*60*2 }, { id: 'q2', title: 'Treino de força — empurrar', desc: 'Supino, desenvolvimento, tríceps. 45 min na academia', category: 'health', rarity: 'common', xp: 50, status: 'open', deadline: 'Hoje', created: Date.now() - 1000*60*60*5 }, { id: 'q3', title: 'Apresentação trimestral para diretoria', desc: 'Slides finais + ensaio cronometrado de 12 min', category: 'work', rarity: 'epic', xp: 350, status: 'open', deadline: 'Amanhã', created: Date.now() - 1000*60*60*24 }, { id: 'q4', title: 'Lançar v1.0 do app pessoal', desc: 'Push final, smoke test, anúncio na newsletter', category: 'side', rarity: 'legendary', xp: 700, status: 'open', deadline: 'Sex', created: Date.now() - 1000*60*60*40 }, { id: 'q5', title: 'Meditação guiada de 10 minutos', desc: 'Reset matinal antes do deep work', category: 'mind', rarity: 'common', xp: 50, status: 'done', deadline: '—', created: Date.now() - 1000*60*60*8, completedAt: Date.now() - 1000*60*60*7 }, { id: 'q6', title: 'Code review do PR #482', desc: 'Comentar e aprovar até o fim da manhã', category: 'work', rarity: 'rare', xp: 150, status: 'done', deadline: '—', created: Date.now() - 1000*60*60*30, completedAt: Date.now() - 1000*60*60*26 }, ]; const SEED_HISTORY = [ { id: 'h1', kind: 'gain', text: 'Meditação guiada concluída — +50 XP', when: '7h atrás' }, { id: 'h2', kind: 'gain', text: 'Code review #482 concluído — +150 XP', when: '26h atrás' }, { id: 'h3', kind: 'level', text: 'Subiu para o Nível 2', when: '1d atrás' }, { id: 'h4', kind: 'gain', text: 'Forjou a missão "Apresentação trimestral"', when: '1d atrás' }, ]; // 20×7 weeks of activity const SEED_HEAT = (() => { const arr = []; let seed = 7; const rng = () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; }; for (let i = 0; i < 140; i++) { const r = rng(); let lvl = 0; if (r > 0.85) lvl = 4; else if (r > 0.65) lvl = 3; else if (r > 0.45) lvl = 2; else if (r > 0.25) lvl = 1; arr.push(lvl); } return arr; })(); window.CATEGORIES = CATEGORIES; window.DIFFICULTIES = DIFFICULTIES; window.TIERS = TIERS; window.BADGES = BADGES; window.RANKING = RANKING; window.SEED_QUESTS = SEED_QUESTS; window.SEED_HISTORY = SEED_HISTORY; window.SEED_HEAT = SEED_HEAT; window.computeLevel = computeLevel; window.tierFor = tierFor; window.xpForLevel = xpForLevel;