// 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;