window.GDD = window.GDD || {}; const { useState, useEffect, useCallback, useRef } = React; function ProgressionDemo() { const [skills, setSkills] = useState([]); const [activeCategory, setActiveCategory] = useState('all'); const [selectedSkill, setSelectedSkill] = useState(null); const [totalXP, setTotalXP] = useState(0); const [xpLog, setXpLog] = useState([]); const [simulating, setSimulating] = useState(false); const simRef = useRef(null); const allSkills = [ // Combat { name: 'Gunnery', category: 'Combat', xp: 380, level: 2, nextLevel: 500 }, { name: 'Missiles', category: 'Combat', xp: 120, level: 1, nextLevel: 500 }, { name: 'Shield Operation', category: 'Combat', xp: 50, level: 0, nextLevel: 100 }, { name: 'Armor Tanking', category: 'Combat', xp: 0, level: 0, nextLevel: 100 }, { name: 'Electronic Warfare', category: 'Combat', xp: 0, level: 0, nextLevel: 100 }, // Industry { name: 'Mining', category: 'Industry', xp: 1850, level: 3, nextLevel: 2000 }, { name: 'Refining', category: 'Industry', xp: 420, level: 2, nextLevel: 500 }, { name: 'Manufacturing', category: 'Industry', xp: 80, level: 0, nextLevel: 100 }, { name: 'Blueprint Research', category: 'Industry', xp: 0, level: 0, nextLevel: 100 }, // Navigation { name: 'Warp Drive Operation', category: 'Navigation', xp: 60, level: 0, nextLevel: 100 }, { name: 'Afterburner', category: 'Navigation', xp: 30, level: 0, nextLevel: 100 }, { name: 'Evasive Maneuvering', category: 'Navigation', xp: 0, level: 0, nextLevel: 100 }, // Trade { name: 'Market Analysis', category: 'Trade', xp: 20, level: 0, nextLevel: 100 }, { name: 'Broker Relations', category: 'Trade', xp: 45, level: 0, nextLevel: 100 }, { name: 'Hauling', category: 'Trade', xp: 0, level: 0, nextLevel: 100 }, // Leadership { name: 'Fleet Command', category: 'Leadership', xp: 0, level: 0, nextLevel: 100 }, { name: 'AI Coordination', category: 'Leadership', xp: 0, level: 0, nextLevel: 100 }, ]; const xpCurve = [100, 500, 2000, 8000, 32000]; const categoryColors = { Combat: 'var(--red)', Industry: 'var(--accent)', Navigation: 'var(--cyan)', Trade: 'var(--green)', Leadership: 'var(--purple)', }; const xpActions = [ { name: 'Mining Cycle', xp: 15, category: 'Industry', desc: 'Complete a mining laser cycle' }, { name: 'NPC Kill', xp: 40, category: 'Combat', desc: 'Destroy an NPC pirate' }, { name: 'Refine Batch', xp: 25, category: 'Industry', desc: 'Refine a batch of ore' }, { name: 'System Jump', xp: 5, category: 'Navigation', desc: 'Jump to a new system' }, { name: 'Market Trade', xp: 20, category: 'Trade', desc: 'Complete a market transaction' }, { name: 'Player Kill', xp: 120, category: 'Combat', desc: 'Destroy a player ship' }, { name: 'Manufacture Item', xp: 35, category: 'Industry', desc: 'Complete a manufacturing job' }, { name: 'Waypoint Route', xp: 30, category: 'Navigation', desc: 'Complete a multi-jump route' }, { name: 'Bounty Collect', xp: 80, category: 'Combat', desc: 'Collect a bounty reward' }, ]; useEffect(() => { setSkills(allSkills.map(s => ({ ...s }))); }, []); useEffect(() => { const total = skills.reduce((sum, s) => sum + s.xp, 0); setTotalXP(total); }, [skills]); const filteredSkills = activeCategory === 'all' ? skills : skills.filter(s => s.category === activeCategory); const categoryStats = Object.keys(categoryColors).map(cat => { const catSkills = skills.filter(s => s.category === cat); const totalXP = catSkills.reduce((sum, s) => sum + s.xp, 0); const maxXP = catSkills.reduce((sum, s) => sum + xpCurve[Math.min(s.level, 4)], 0); const avgLevel = catSkills.length > 0 ? catSkills.reduce((sum, s) => sum + s.level, 0) / catSkills.length : 0; return { category: cat, color: categoryColors[cat], totalXP, maxXP, avgLevel, count: catSkills.length }; }); const handleSimulate = useCallback(() => { if (simulating) { setSimulating(false); if (simRef.current) clearInterval(simRef.current); return; } setSimulating(true); simRef.current = setInterval(() => { const action = xpActions[Math.floor(Math.random() * xpActions.length)]; const skillName = action.category === 'Combat' ? 'Gunnery' : action.category === 'Industry' ? 'Mining' : action.category === 'Navigation' ? 'Warp Drive Operation' : action.category === 'Trade' ? 'Broker Relations' : 'Fleet Command'; setSkills(prev => prev.map(s => { if (s.name !== skillName) return s; let newXp = s.xp + action.xp; let newLevel = s.level; while (newLevel < 5 && newXp >= xpCurve[newLevel]) { newXp -= xpCurve[newLevel]; newLevel++; } return { ...s, xp: newXp, level: newLevel, nextLevel: xpCurve[Math.min(newLevel, 4)] }; })); setXpLog(prev => [{ action: action.name, xp: action.xp, skill: skillName, time: new Date().toLocaleTimeString('en', { hour12: false }), }, ...prev.slice(0, 19)]); }, 800); }, [simulating]); useEffect(() => { return () => { if (simRef.current) clearInterval(simRef.current); }; }, []); const levelColor = (lvl) => { if (lvl === 0) return 'var(--muted)'; if (lvl === 1) return 'var(--green)'; if (lvl === 2) return 'var(--cyan)'; if (lvl === 3) return 'var(--purple)'; if (lvl === 4) return 'var(--accent)'; return 'var(--red)'; }; return (
e.currentTarget.style.color='var(--fg-bright)'} onMouseLeave={e => e.currentTarget.style.color='var(--muted)'}>← Back to Docs

Skill Progression Demo

Action-based XP system across 5 categories and 17+ skills. Hit the simulate button to watch XP flow in from random activities — each action awards XP to the matching skill category.

{/* HUD-style progression strip */}
SKILL PROGRESSION
TOTAL XP {totalXP.toLocaleString()}
TRAINED {skills.filter(s => s.level > 0).length}/{skills.length}
MAX LVL {Math.max(...skills.map(s => s.level))} {simulating && ● SIMULATING}
{/* Stats */}
{totalXP.toLocaleString()}
Total XP
{skills.filter(s => s.level > 0).length}/{skills.length}
Skills Trained
{Math.max(...skills.map(s => s.level))}
Highest Level
{xpLog.length}
Actions (session)
{/* Simulate button */}
Generates random XP actions every 800ms
{/* Category overview */}
{categoryStats.map(cat => (
setActiveCategory(activeCategory === cat.category ? 'all' : cat.category)}>

{cat.category}

avg Lvl {cat.avgLevel.toFixed(1)}
0 ? (cat.totalXP / (cat.maxXP * 2)) * 100 : 0}%`, background: cat.color, }} />
{cat.totalXP.toLocaleString()} XP · {cat.count} skills
))}
{Object.keys(categoryColors).map(cat => ( ))}
{/* Skill tree */}
{filteredSkills.map((skill, i) => { const progress = skill.level >= 5 ? 100 : (skill.xp / skill.nextLevel) * 100; return (
setSelectedSkill(skill)}>
{skill.name} Lvl {skill.level}
{skill.category}
{skill.level >= 5 ? 'MAX' : `${skill.xp.toLocaleString()} / ${skill.nextLevel.toLocaleString()} XP`}
); })}
{/* XP log + detail */}
{/* Selected skill detail */} {selectedSkill && (

{selectedSkill.name}

Current Level Level {selectedSkill.level}{selectedSkill.level >= 5 ? ' (MAX)' : ''}
Category {selectedSkill.category}
XP to Next Level {selectedSkill.level >= 5 ? '—' : `${selectedSkill.xp.toLocaleString()} / ${selectedSkill.nextLevel.toLocaleString()}`}
{/* Level milestone visualization */}
{[0, 1, 2, 3, 4].map(lvl => (
lvl ? categoryColors[selectedSkill.category] + '20' : 'var(--surface-raised)', border: `1px solid ${selectedSkill.level > lvl ? categoryColors[selectedSkill.category] + '40' : 'var(--border)'}`, borderRadius: 'var(--radius-md)', }}>
lvl ? levelColor(lvl + 1) : 'var(--muted)' }}> {lvl + 1}
{xpCurve[lvl].toLocaleString()} XP
))}
)} {/* XP activity log */}

XP Activity Log

{xpLog.length === 0 && (
Start the simulation to see XP flow in real-time.
)} {xpLog.map((entry, i) => (
{entry.time} {entry.action} +{entry.xp} XP → {entry.skill}
))}
{/* XP actions reference */}

XP Sources

{xpActions.map((action, i) => (
{action.name} {action.desc}
{action.category} +{action.xp}
))}
); } window.GDD.ProgressionDemo = ProgressionDemo;