feat: Implement cumulative XP leveling system with new helper functions and update XP bar to show progress within the current level.
This commit is contained in:
@@ -97,9 +97,12 @@ export async function generateStudentIdCard(data: StudentCardData): Promise<Buff
|
||||
ctx.restore();
|
||||
|
||||
// Draw XP Bar
|
||||
const xpForNextLevel = levelingService.getXpForLevel(data.level);
|
||||
const xpForThisLevel = levelingService.getXpForNextLevel(data.level); // The total size of the current level bucket
|
||||
const xpAtStartOfLevel = levelingService.getXpToReachLevel(data.level); // The accumulated XP when this level started
|
||||
const currentLevelProgress = Number(data.xp) - xpAtStartOfLevel; // How much XP into this level
|
||||
|
||||
const xpBarMaxWidth = 382;
|
||||
const xpBarWidth = xpBarMaxWidth * Number(data.xp) / Number(xpForNextLevel);
|
||||
const xpBarWidth = Math.max(0, Math.min(xpBarMaxWidth, xpBarMaxWidth * currentLevelProgress / xpForThisLevel));
|
||||
const xpBarHeight = 3;
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#B3AD93';
|
||||
|
||||
@@ -5,12 +5,42 @@ import { config } from "@/lib/config";
|
||||
import type { Transaction } from "@/lib/types";
|
||||
|
||||
export const levelingService = {
|
||||
// Calculate XP required for a specific level
|
||||
getXpForLevel: (level: number) => {
|
||||
return Math.floor(config.leveling.base * Math.pow(level, config.leveling.exponent));
|
||||
// Calculate total XP required to REACH a specific level (Cumulative)
|
||||
// Level 1 = 0 XP
|
||||
// Level 2 = Base * (1^Exp)
|
||||
// Level 3 = Level 2 + Base * (2^Exp)
|
||||
// ...
|
||||
getXpToReachLevel: (level: number) => {
|
||||
let total = 0;
|
||||
for (let l = 1; l < level; l++) {
|
||||
total += Math.floor(config.leveling.base * Math.pow(l, config.leveling.exponent));
|
||||
}
|
||||
return total;
|
||||
},
|
||||
|
||||
// Pure XP addition - No cooldown checks
|
||||
// Calculate level from Total XP
|
||||
getLevelFromXp: (totalXp: bigint) => {
|
||||
let level = 1;
|
||||
let xp = Number(totalXp);
|
||||
|
||||
while (true) {
|
||||
// XP needed to complete current level and reach next
|
||||
const xpForNext = Math.floor(config.leveling.base * Math.pow(level, config.leveling.exponent));
|
||||
if (xp < xpForNext) {
|
||||
return level;
|
||||
}
|
||||
xp -= xpForNext;
|
||||
level++;
|
||||
}
|
||||
},
|
||||
|
||||
// Get XP needed to complete the current level (for calculating next level threshold in isolation)
|
||||
// Used internally or for display
|
||||
getXpForNextLevel: (currentLevel: number) => {
|
||||
return Math.floor(config.leveling.base * Math.pow(currentLevel, config.leveling.exponent));
|
||||
},
|
||||
|
||||
// Cumulative XP addition
|
||||
addXp: async (id: string, amount: bigint, tx?: Transaction) => {
|
||||
return await withTransaction(async (txFn) => {
|
||||
// Get current state
|
||||
@@ -20,30 +50,24 @@ export const levelingService = {
|
||||
|
||||
if (!user) throw new Error("User not found");
|
||||
|
||||
let newXp = (user.xp ?? 0n) + amount;
|
||||
let currentLevel = user.level ?? 1;
|
||||
let levelUp = false;
|
||||
const currentXp = user.xp ?? 0n;
|
||||
const newXp = currentXp + amount;
|
||||
|
||||
// Check for level up loop
|
||||
let xpForNextLevel = BigInt(levelingService.getXpForLevel(currentLevel));
|
||||
|
||||
while (newXp >= xpForNextLevel) {
|
||||
newXp -= xpForNextLevel;
|
||||
currentLevel++;
|
||||
levelUp = true;
|
||||
xpForNextLevel = BigInt(levelingService.getXpForLevel(currentLevel));
|
||||
}
|
||||
// Calculate new level based on TOTAL accumulated XP
|
||||
const newLevel = levelingService.getLevelFromXp(newXp);
|
||||
const currentLevel = user.level ?? 1;
|
||||
const levelUp = newLevel > currentLevel;
|
||||
|
||||
// Update user
|
||||
const [updatedUser] = await txFn.update(users)
|
||||
.set({
|
||||
xp: newXp,
|
||||
level: currentLevel,
|
||||
level: newLevel,
|
||||
})
|
||||
.where(eq(users.id, BigInt(id)))
|
||||
.returning();
|
||||
|
||||
return { user: updatedUser, levelUp, currentLevel };
|
||||
return { user: updatedUser, levelUp, currentLevel: newLevel };
|
||||
}, tx);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user