pokebuddy/src/progression.js
2026-04-26 15:57:41 -03:00

70 lines
1.9 KiB
JavaScript

export const XP_REWARDS = Object.freeze({
task_started: 2,
task_done: 25,
task_cancelled: 1,
command_success: 6,
command_failure: 2,
tests_passed: 18,
tests_failed: 4,
build_passed: 16,
build_failed: 3,
lint_passed: 12,
lint_failed: 3,
typecheck_passed: 12,
typecheck_failed: 3,
commit_created: 20,
merge_completed: 28,
push_attempted: 8,
docs_written: 15,
watch_changed: 4,
manual_focus: 10
});
export const LEVEL_TITLES = Object.freeze([
{ min: 1, title: 'Hatchling' },
{ min: 2, title: 'Junior Companion' },
{ min: 5, title: 'Debug Partner' },
{ min: 10, title: 'Terminal Guardian' },
{ min: 20, title: 'Senior Beast' },
{ min: 35, title: 'Mythic Companion' },
{ min: 50, title: 'Legend of the Prompt' }
]);
export function xpForNextLevel(level) {
return Math.max(1, level) * Math.max(1, level) * 100;
}
export function levelFromXp(xp = 0) {
let level = 1;
while (xp >= xpForNextLevel(level)) {
level += 1;
}
return level;
}
export function titleForLevel(level = 1) {
return LEVEL_TITLES.filter((entry) => level >= entry.min).at(-1)?.title ?? 'Hatchling';
}
export function progressToNextLevel(xp = 0) {
const level = levelFromXp(xp);
const previous = level <= 1 ? 0 : xpForNextLevel(level - 1);
const next = xpForNextLevel(level);
return {
level,
title: titleForLevel(level),
current: Math.max(0, xp - previous),
needed: Math.max(1, next - previous),
previous,
next
};
}
export function applyXp(entry, amount) {
const beforeLevel = entry.level ?? levelFromXp(entry.xp ?? 0);
entry.xp = Math.max(0, (entry.xp ?? 0) + amount);
entry.level = levelFromXp(entry.xp);
entry.energy = Math.min(100, Math.max(0, (entry.energy ?? 50) + Math.min(12, Math.ceil(amount / 3))));
entry.mood = entry.level > beforeLevel ? 'level-up' : amount >= 15 ? 'proud' : 'focused';
return { beforeLevel, afterLevel: entry.level, leveledUp: entry.level > beforeLevel };
}