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.

PadawanForge v1.4.1