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();
|
ctx.restore();
|
||||||
|
|
||||||
// Draw XP Bar
|
// 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 xpBarMaxWidth = 382;
|
||||||
const xpBarWidth = xpBarMaxWidth * Number(data.xp) / Number(xpForNextLevel);
|
const xpBarWidth = Math.max(0, Math.min(xpBarMaxWidth, xpBarMaxWidth * currentLevelProgress / xpForThisLevel));
|
||||||
const xpBarHeight = 3;
|
const xpBarHeight = 3;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.fillStyle = '#B3AD93';
|
ctx.fillStyle = '#B3AD93';
|
||||||
|
|||||||
@@ -5,12 +5,42 @@ import { config } from "@/lib/config";
|
|||||||
import type { Transaction } from "@/lib/types";
|
import type { Transaction } from "@/lib/types";
|
||||||
|
|
||||||
export const levelingService = {
|
export const levelingService = {
|
||||||
// Calculate XP required for a specific level
|
// Calculate total XP required to REACH a specific level (Cumulative)
|
||||||
getXpForLevel: (level: number) => {
|
// Level 1 = 0 XP
|
||||||
return Math.floor(config.leveling.base * Math.pow(level, config.leveling.exponent));
|
// 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) => {
|
addXp: async (id: string, amount: bigint, tx?: Transaction) => {
|
||||||
return await withTransaction(async (txFn) => {
|
return await withTransaction(async (txFn) => {
|
||||||
// Get current state
|
// Get current state
|
||||||
@@ -20,30 +50,24 @@ export const levelingService = {
|
|||||||
|
|
||||||
if (!user) throw new Error("User not found");
|
if (!user) throw new Error("User not found");
|
||||||
|
|
||||||
let newXp = (user.xp ?? 0n) + amount;
|
const currentXp = user.xp ?? 0n;
|
||||||
let currentLevel = user.level ?? 1;
|
const newXp = currentXp + amount;
|
||||||
let levelUp = false;
|
|
||||||
|
|
||||||
// Check for level up loop
|
// Calculate new level based on TOTAL accumulated XP
|
||||||
let xpForNextLevel = BigInt(levelingService.getXpForLevel(currentLevel));
|
const newLevel = levelingService.getLevelFromXp(newXp);
|
||||||
|
const currentLevel = user.level ?? 1;
|
||||||
while (newXp >= xpForNextLevel) {
|
const levelUp = newLevel > currentLevel;
|
||||||
newXp -= xpForNextLevel;
|
|
||||||
currentLevel++;
|
|
||||||
levelUp = true;
|
|
||||||
xpForNextLevel = BigInt(levelingService.getXpForLevel(currentLevel));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update user
|
// Update user
|
||||||
const [updatedUser] = await txFn.update(users)
|
const [updatedUser] = await txFn.update(users)
|
||||||
.set({
|
.set({
|
||||||
xp: newXp,
|
xp: newXp,
|
||||||
level: currentLevel,
|
level: newLevel,
|
||||||
})
|
})
|
||||||
.where(eq(users.id, BigInt(id)))
|
.where(eq(users.id, BigInt(id)))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return { user: updatedUser, levelUp, currentLevel };
|
return { user: updatedUser, levelUp, currentLevel: newLevel };
|
||||||
}, tx);
|
}, tx);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user