Level & Experience System
Overview
PadawanForge implements a comprehensive level and experience system that provides player progression, achievement tracking, and gamification features. This system calculates levels based on experience points, manages progression, and provides detailed player statistics.
Architecture
Core Components
- Level Calculation: Experience-based level determination
- Experience Rewards: Configurable XP rewards for actions
- Rank System: Player ranking with titles
- Progress Tracking: Detailed progression analytics
- Achievement Integration: Experience rewards for achievements
System Features
- Progressive Difficulty: Higher levels require more experience
- Rank Titles: Automatic title assignment based on level
- Experience Rewards: Configurable rewards for various actions
- Progress Analytics: Detailed progression tracking
- Achievement Integration: XP rewards for unlocking achievements
Implementation
Level Calculation Functions
import {
calculateLevel,
getExperienceForLevel,
getExperienceToNextLevel,
getExperienceProgress,
getPlayerRank,
getExperienceRewards
} from '@/lib/utils/levelSystem';
// Calculate player level from experience
const level = calculateLevel(1250); // Returns 4
// Get experience required for a specific level
const expForLevel = getExperienceForLevel(5); // Returns 1600
// Get experience needed to reach next level
const expToNext = getExperienceToNextLevel(1250); // Returns 350
// Get detailed progress information
const progress = getExperienceProgress(1250);
console.log(progress);
// {
// currentLevel: 4,
// nextLevel: 5,
// expForCurrentLevel: 900,
// expForNextLevel: 1600,
// expToNextLevel: 350,
// progressPercent: 50
// }
Experience Rewards Configuration
// Get experience rewards for different actions
const rewards = getExperienceRewards();
console.log(rewards);
// {
// 'chat_message': 1,
// 'npc_interaction': 5,
// 'daily_login': 10,
// 'first_login': 100,
// 'create_npc': 25,
// 'lobby_participation': 2,
// 'achievement_unlock': 50,
// 'registration_completed': 200,
// 'tutorial_completed': 200
// }
Usage Examples
Basic Level Management
import { calculateLevel, getExperienceProgress } from '@/lib/utils/levelSystem';
class PlayerLevelManager {
// Get player level information
getPlayerLevelInfo(experience: number) {
const level = calculateLevel(experience);
const progress = getExperienceProgress(experience);
const rank = getPlayerRank(experience);
return {
level,
rank,
experience,
progress: {
currentLevel: progress.currentLevel,
nextLevel: progress.nextLevel,
progressPercent: progress.progressPercent,
expToNextLevel: progress.expToNextLevel
}
};
}
// Check if player leveled up
checkLevelUp(oldExperience: number, newExperience: number) {
const oldLevel = calculateLevel(oldExperience);
const newLevel = calculateLevel(newExperience);
return {
leveledUp: newLevel > oldLevel,
oldLevel,
newLevel,
levelsGained: newLevel - oldLevel
};
}
// Calculate experience needed for target level
getExperienceToTarget(currentExp: number, targetLevel: number) {
const currentLevel = calculateLevel(currentExp);
if (targetLevel <= currentLevel) {
return 0;
}
const targetExp = getExperienceForLevel(targetLevel);
return targetExp - currentExp;
}
}
Experience Service Integration
import { ExperienceService } from '@/lib/services/ExperienceService';
class GameExperienceManager {
private experienceService: ExperienceService;
constructor(db: D1Database) {
this.experienceService = new ExperienceService(db);
}
// Award experience for various actions
async awardExperienceForAction(playerUuid: string, action: string) {
const rewards = getExperienceRewards();
const expAmount = rewards[action] || 0;
if (expAmount > 0) {
const result = await this.experienceService.awardExperience(
playerUuid,
expAmount,
action
);
return {
experienceGained: expAmount,
newTotal: result.totalExperience,
leveledUp: result.leveledUp,
newLevel: result.newLevel
};
}
return null;
}
// Award experience for chat message
async awardChatExperience(playerUuid: string) {
return await this.awardExperienceForAction(playerUuid, 'chat_message');
}
// Award experience for NPC interaction
async awardNPCInteractionExperience(playerUuid: string) {
return await this.awardExperienceForAction(playerUuid, 'npc_interaction');
}
// Award experience for daily login
async awardDailyLoginExperience(playerUuid: string) {
return await this.awardExperienceForAction(playerUuid, 'daily_login');
}
// Award experience for achievement
async awardAchievementExperience(playerUuid: string, achievementName: string) {
const result = await this.awardExperienceForAction(playerUuid, 'achievement_unlock');
if (result) {
// Log achievement unlock
await this.logAchievementUnlock(playerUuid, achievementName, result.experienceGained);
}
return result;
}
}
Player Statistics and Analytics
class PlayerAnalytics {
private experienceService: ExperienceService;
constructor(db: D1Database) {
this.experienceService = new ExperienceService(db);
}
// Get comprehensive player stats
async getPlayerStats(playerUuid: string) {
const stats = await this.experienceService.getPlayerStats(playerUuid);
return {
player: stats.player,
totalEarned: stats.totalEarned,
currentRank: stats.currentRank,
breakdown: stats.breakdown,
levelInfo: getExperienceProgress(stats.player.experience),
rank: getPlayerRank(stats.player.experience)
};
}
// Get experience history
async getExperienceHistory(playerUuid: string, limit: number = 50) {
return await this.experienceService.getExperienceHistory(playerUuid, limit);
}
// Get leaderboard
async getLeaderboard(limit: number = 10) {
return await this.experienceService.getLeaderboard(limit);
}
// Get player's rank position
async getPlayerRankPosition(playerUuid: string) {
const leaderboard = await this.getLeaderboard(1000); // Get large leaderboard
const playerIndex = leaderboard.findIndex(p => p.uuid === playerUuid);
return playerIndex >= 0 ? playerIndex + 1 : null;
}
}
Level Calculation Algorithm
Experience Formula
// Level calculation: level = floor(sqrt(experience / 100)) + 1
function calculateLevel(experience: number): number {
return Math.floor(Math.sqrt(experience / 100)) + 1;
}
// Experience required for level: (level - 1)^2 * 100
function getExperienceForLevel(level: number): number {
return Math.pow(level - 1, 2) * 100;
}
// Experience to next level
function getExperienceToNextLevel(currentExp: number): number {
const currentLevel = calculateLevel(currentExp);
const nextLevelExp = getExperienceForLevel(currentLevel + 1);
return nextLevelExp - currentExp;
}
Level Progression Examples
// Level progression examples
const levelExamples = [
{ level: 1, exp: 0, expToNext: 100 },
{ level: 2, exp: 100, expToNext: 300 },
{ level: 3, exp: 400, expToNext: 500 },
{ level: 4, exp: 900, expToNext: 700 },
{ level: 5, exp: 1600, expToNext: 900 },
{ level: 10, exp: 8100, expToNext: 1900 },
{ level: 20, exp: 36100, expToNext: 3900 },
{ level: 50, exp: 240100, expToNext: 9900 },
{ level: 100, exp: 980100, expToNext: 19900 }
];
// Calculate progression for a player
function calculatePlayerProgression(currentExp: number) {
const currentLevel = calculateLevel(currentExp);
const progress = getExperienceProgress(currentExp);
return {
currentLevel,
currentExp,
progressPercent: progress.progressPercent,
expToNextLevel: progress.expToNextLevel,
rank: getPlayerRank(currentExp),
nextMilestone: getExperienceForLevel(currentLevel + 1)
};
}
Rank System
Rank Titles
// Get player rank based on level
function getPlayerRank(experience: number): string {
const level = calculateLevel(experience);
if (level < 5) return 'Novice';
if (level < 10) return 'Apprentice';
if (level < 20) return 'Skilled';
if (level < 35) return 'Expert';
if (level < 50) return 'Master';
if (level < 75) return 'Grandmaster';
if (level < 100) return 'Legend';
return 'Mythic';
}
// Rank requirements
const rankRequirements = {
'Novice': { minLevel: 1, maxLevel: 4 },
'Apprentice': { minLevel: 5, maxLevel: 9 },
'Skilled': { minLevel: 10, maxLevel: 19 },
'Expert': { minLevel: 20, maxLevel: 34 },
'Master': { minLevel: 35, maxLevel: 49 },
'Grandmaster': { minLevel: 50, maxLevel: 74 },
'Legend': { minLevel: 75, maxLevel: 99 },
'Mythic': { minLevel: 100, maxLevel: Infinity }
};
Rank Badge System
// Get rank badge information
function getRankBadge(rank: string) {
const badges = {
'Novice': { color: 'gray', icon: '🌟', title: 'Novice' },
'Apprentice': { color: 'green', icon: '🌱', title: 'Apprentice' },
'Skilled': { color: 'blue', icon: '⚔️', title: 'Skilled' },
'Expert': { color: 'purple', icon: '🎯', title: 'Expert' },
'Master': { color: 'orange', icon: '👑', title: 'Master' },
'Grandmaster': { color: 'red', icon: '🔥', title: 'Grandmaster' },
'Legend': { color: 'gold', icon: '⭐', title: 'Legend' },
'Mythic': { color: 'rainbow', icon: '✨', title: 'Mythic' }
};
return badges[rank] || badges['Novice'];
}
Experience Rewards System
Action-Based Rewards
// Experience rewards for different actions
const experienceRewards = {
// Communication
'chat_message': 1,
'direct_message': 2,
'lobby_participation': 2,
// NPC Interactions
'npc_interaction': 5,
'npc_question': 3,
'npc_challenge': 10,
// Game Activities
'game_session_complete': 25,
'perfect_score': 50,
'streak_bonus': 5,
// Social Activities
'help_other_player': 15,
'create_npc': 25,
'share_knowledge': 10,
// Daily Activities
'daily_login': 10,
'weekly_challenge': 100,
'monthly_goal': 500,
// Milestones
'first_login': 100,
'registration_completed': 200,
'tutorial_completed': 200,
'achievement_unlock': 50,
'level_milestone': 100
};
// Calculate bonus experience
function calculateBonusExperience(baseExp: number, multiplier: number = 1.0) {
return Math.floor(baseExp * multiplier);
}
// Award experience with bonuses
async function awardExperienceWithBonus(playerUuid: string, action: string, bonusMultiplier: number = 1.0) {
const baseExp = experienceRewards[action] || 0;
const totalExp = calculateBonusExperience(baseExp, bonusMultiplier);
if (totalExp > 0) {
return await experienceService.awardExperience(playerUuid, totalExp, action);
}
return null;
}
Streak Bonuses
// Streak bonus system
class StreakManager {
private db: D1Database;
constructor(db: D1Database) {
this.db = db;
}
// Get player's current streak
async getPlayerStreak(playerUuid: string): Promise<number> {
const result = await this.db.prepare(`
SELECT streak_count FROM player_streaks
WHERE player_uuid = ? AND streak_type = 'daily_login'
`).bind(playerUuid).first();
return result?.streak_count || 0;
}
// Calculate streak bonus
calculateStreakBonus(streakCount: number): number {
if (streakCount < 3) return 1.0;
if (streakCount < 7) return 1.2;
if (streakCount < 14) return 1.5;
if (streakCount < 30) return 2.0;
return 3.0; // 30+ days
}
// Award experience with streak bonus
async awardExperienceWithStreak(playerUuid: string, action: string) {
const streakCount = await this.getPlayerStreak(playerUuid);
const streakBonus = this.calculateStreakBonus(streakCount);
return await awardExperienceWithBonus(playerUuid, action, streakBonus);
}
}
Progress Tracking
Experience History
// Track experience gains
class ExperienceTracker {
private db: D1Database;
constructor(db: D1Database) {
this.db = db;
}
// Log experience gain
async logExperienceGain(playerUuid: string, amount: number, reason: string) {
await this.db.prepare(`
INSERT INTO experience_log (player_uuid, amount, reason, timestamp)
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
`).bind(playerUuid, amount, reason).run();
}
// Get experience breakdown by reason
async getExperienceBreakdown(playerUuid: string) {
const result = await this.db.prepare(`
SELECT reason, SUM(amount) as total, COUNT(*) as count
FROM experience_log
WHERE player_uuid = ?
GROUP BY reason
ORDER BY total DESC
`).bind(playerUuid).all();
return result.results;
}
// Get recent experience gains
async getRecentExperience(playerUuid: string, limit: number = 10) {
const result = await this.db.prepare(`
SELECT amount, reason, timestamp
FROM experience_log
WHERE player_uuid = ?
ORDER BY timestamp DESC
LIMIT ?
`).bind(playerUuid, limit).all();
return result.results;
}
}
Level Progression Analytics
// Analyze level progression
class ProgressionAnalytics {
private db: D1Database;
constructor(db: D1Database) {
this.db = db;
}
// Get level progression over time
async getLevelProgression(playerUuid: string) {
const result = await this.db.prepare(`
SELECT
DATE(timestamp) as date,
SUM(amount) as daily_exp,
COUNT(*) as actions_count
FROM experience_log
WHERE player_uuid = ?
GROUP BY DATE(timestamp)
ORDER BY date DESC
LIMIT 30
`).bind(playerUuid).all();
return result.results.map(day => ({
date: day.date,
dailyExp: day.daily_exp,
actionsCount: day.actions_count,
level: calculateLevel(day.daily_exp)
}));
}
// Get average daily experience
async getAverageDailyExperience(playerUuid: string) {
const result = await this.db.prepare(`
SELECT AVG(daily_exp) as avg_daily_exp
FROM (
SELECT DATE(timestamp) as date, SUM(amount) as daily_exp
FROM experience_log
WHERE player_uuid = ?
GROUP BY DATE(timestamp)
)
`).bind(playerUuid).first();
return result?.avg_daily_exp || 0;
}
// Predict level up date
async predictLevelUpDate(playerUuid: string) {
const currentExp = await this.getCurrentExperience(playerUuid);
const currentLevel = calculateLevel(currentExp);
const nextLevelExp = getExperienceForLevel(currentLevel + 1);
const expNeeded = nextLevelExp - currentExp;
const avgDailyExp = await this.getAverageDailyExperience(playerUuid);
if (avgDailyExp <= 0) return null;
const daysToLevelUp = Math.ceil(expNeeded / avgDailyExp);
const levelUpDate = new Date();
levelUpDate.setDate(levelUpDate.getDate() + daysToLevelUp);
return levelUpDate;
}
}
Component Integration
Level Display Component
import { useState, useEffect } from 'react';
import { getExperienceProgress, getPlayerRank } from '@/lib/utils/levelSystem';
interface LevelDisplayProps {
experience: number;
showProgress?: boolean;
showRank?: boolean;
}
function LevelDisplay({ experience, showProgress = true, showRank = true }: LevelDisplayProps) {
const [progress, setProgress] = useState<any>(null);
const [rank, setRank] = useState<string>('');
useEffect(() => {
const progressData = getExperienceProgress(experience);
const rankData = getPlayerRank(experience);
setProgress(progressData);
setRank(rankData);
}, [experience]);
if (!progress) return <div>Loading...</div>;
return (
<div className="level-display">
<div className="level-info">
<span className="level">Level {progress.currentLevel}</span>
{showRank && <span className="rank">{rank}</span>}
</div>
{showProgress && (
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress.progressPercent}%` }}
/>
<span className="progress-text">
{progress.expToNextLevel} XP to next level
</span>
</div>
)}
<div className="experience-info">
<span className="total-exp">{experience} Total XP</span>
</div>
</div>
);
}
Experience Gain Animation
import { useState, useEffect } from 'react';
interface ExperienceGainProps {
amount: number;
onComplete?: () => void;
}
function ExperienceGain({ amount, onComplete }: ExperienceGainProps) {
const [visible, setVisible] = useState(false);
const [animationComplete, setAnimationComplete] = useState(false);
useEffect(() => {
setVisible(true);
const timer = setTimeout(() => {
setAnimationComplete(true);
onComplete?.();
}, 2000);
return () => clearTimeout(timer);
}, [onComplete]);
if (!visible) return null;
return (
<div className={`experience-gain ${animationComplete ? 'fade-out' : 'fade-in'}`}>
<span className="exp-amount">+{amount} XP</span>
<span className="exp-icon">⭐</span>
</div>
);
}
Testing
Level System Testing
describe('Level System', () => {
it('should calculate levels correctly', () => {
expect(calculateLevel(0)).toBe(1);
expect(calculateLevel(100)).toBe(2);
expect(calculateLevel(400)).toBe(3);
expect(calculateLevel(900)).toBe(4);
expect(calculateLevel(1600)).toBe(5);
});
it('should calculate experience requirements correctly', () => {
expect(getExperienceForLevel(1)).toBe(0);
expect(getExperienceForLevel(2)).toBe(100);
expect(getExperienceForLevel(3)).toBe(400);
expect(getExperienceForLevel(4)).toBe(900);
expect(getExperienceForLevel(5)).toBe(1600);
});
it('should calculate progress correctly', () => {
const progress = getExperienceProgress(1250);
expect(progress.currentLevel).toBe(4);
expect(progress.nextLevel).toBe(5);
expect(progress.progressPercent).toBe(50);
expect(progress.expToNextLevel).toBe(350);
});
it('should assign ranks correctly', () => {
expect(getPlayerRank(0)).toBe('Novice');
expect(getPlayerRank(400)).toBe('Apprentice');
expect(getPlayerRank(1600)).toBe('Skilled');
expect(getPlayerRank(8100)).toBe('Expert');
expect(getPlayerRank(240100)).toBe('Master');
});
});
This level and experience system provides comprehensive player progression tracking with detailed analytics and gamification features.