diff --git a/index.html b/index.html index d2f2124..0e9d907 100644 --- a/index.html +++ b/index.html @@ -3550,56 +3550,9 @@ } }; - const PLAYER_CLASSES = { - 'trail_runner': { - name: 'Trail Runner', - icon: '🏃', - available: true, - description: 'Masters of endurance and speed', - baseStats: { hp: 100, mp: 50, atk: 12, def: 8 }, - hpPerLevel: 20, - mpPerLevel: 10, - atkPerLevel: 3, - defPerLevel: 2, - skills: ['basic_attack', 'brand_new_hokas', 'runners_high', 'shin_kick', 'whirlwind'] - }, - 'gym_bro': { - name: 'Gym Bro', - icon: '💪', - available: false, - description: 'Raw power and protein', - baseStats: { hp: 130, mp: 30, atk: 18, def: 10 }, - hpPerLevel: 25, - mpPerLevel: 5, - atkPerLevel: 5, - defPerLevel: 3, - skills: [] - }, - 'yoga_master': { - name: 'Yoga Master', - icon: '🧘', - available: false, - description: 'Flexible and mystical', - baseStats: { hp: 80, mp: 80, atk: 8, def: 6 }, - hpPerLevel: 15, - mpPerLevel: 15, - atkPerLevel: 2, - defPerLevel: 1, - skills: [] - }, - 'crossfit_crusader': { - name: 'CrossFit Crusader', - icon: '🏋️', - available: false, - description: 'Jack of all workouts', - baseStats: { hp: 110, mp: 40, atk: 15, def: 12 }, - hpPerLevel: 22, - mpPerLevel: 8, - atkPerLevel: 4, - defPerLevel: 3, - skills: [] - } - }; + // Player classes (loaded from database via API) + let PLAYER_CLASSES = {}; + let classesLoaded = false; // Skill definitions const SKILLS = { @@ -3690,14 +3643,8 @@ } }; - // Skill pools for skill selection at level-up milestones - const SKILL_POOLS = { - 'trail_runner': { - 2: ['brand_new_hokas', 'quick_step'], // Level 2 choice - 3: ['runners_high', 'second_wind'], // Level 3 choice - 5: ['shin_kick', 'finish_line_sprint'] // Level 5 choice - } - }; + // Skill pools for skill selection at level-up milestones (loaded from database) + let SKILL_POOLS = {}; // Monster type definitions (loaded from database via API) let MONSTER_TYPES = {}; @@ -3813,6 +3760,78 @@ return []; } + // Load player classes from database + async function loadClasses() { + try { + const response = await fetch('/api/classes'); + if (response.ok) { + const classes = await response.json(); + + for (const cls of classes) { + // Load skills for this class + const skillsResponse = await fetch(`/api/classes/${cls.id}/skills`); + let classSkills = []; + if (skillsResponse.ok) { + classSkills = await skillsResponse.json(); + } + + // Build the class object + PLAYER_CLASSES[cls.id] = { + name: cls.name, + icon: getClassIcon(cls.id), + available: cls.enabled === 1, + description: cls.description, + baseStats: { + hp: cls.base_hp, + mp: cls.base_mp, + atk: cls.base_atk, + def: cls.base_def + }, + baseAccuracy: cls.base_accuracy || 90, + baseDodge: cls.base_dodge || 10, + hpPerLevel: cls.hp_per_level, + mpPerLevel: cls.mp_per_level, + atkPerLevel: cls.atk_per_level, + defPerLevel: cls.def_per_level, + skills: classSkills + .filter(s => !s.choice_group) // Auto-learned skills (no choice) + .map(s => s.custom_name ? s.skill_id : s.skill_id) + }; + + // Build SKILL_POOLS from skills with choice_groups + SKILL_POOLS[cls.id] = {}; + classSkills.forEach(skill => { + if (skill.choice_group) { + const level = skill.unlock_level; + if (!SKILL_POOLS[cls.id][level]) { + SKILL_POOLS[cls.id][level] = []; + } + // Use custom name as skill ID for display, but store skill_id + SKILL_POOLS[cls.id][level].push(skill.skill_id); + } + }); + } + + classesLoaded = true; + console.log('Loaded classes from database:', Object.keys(PLAYER_CLASSES)); + console.log('Built skill pools:', SKILL_POOLS); + } + } catch (err) { + console.error('Failed to load classes:', err); + } + } + + // Get icon for a class (can be extended to support custom icons in DB later) + function getClassIcon(classId) { + const icons = { + 'trail_runner': '🏃', + 'gym_bro': '💪', + 'yoga_master': '🧘', + 'crossfit_crusader': '🏋️' + }; + return icons[classId] || '⚔️'; + } + // Get skill display name for a class (or base name if no custom) function getSkillForClass(skillId, classId) { const baseSkill = SKILLS_DB[skillId] || SKILLS[skillId]; @@ -9814,15 +9833,24 @@ pendingSkillChoice = { level, options }; const optionsHtml = options.map(skillId => { - const skill = SKILLS[skillId]; + // Get skill with class-specific name + const skillInfo = getSkillForClass(skillId, playerStats.class); + const hardcodedSkill = SKILLS[skillId]; + const skill = skillInfo || hardcodedSkill; if (!skill) return ''; + + const displayName = skillInfo?.displayName || skill.name; + const displayDesc = skillInfo?.displayDescription || skill.description; + const icon = hardcodedSkill?.icon || '⚔️'; + const mpCost = skill.mpCost || skill.mp_cost || 0; + return `
- ${skill.icon} + ${icon}
-
${skill.name}
-
${skill.description}
-
${skill.mpCost} MP
+
${displayName}
+
${displayDesc}
+
${mpCost} MP
`; @@ -9852,9 +9880,11 @@ document.getElementById('skillChoiceModal').style.display = 'none'; pendingSkillChoice = null; - // Show notification - const skill = SKILLS[skillId]; - showNotification(`Learned ${skill.name}!`, 'success'); + // Show notification with class-specific name + const skillInfo = getSkillForClass(skillId, playerStats.class); + const skill = skillInfo || SKILLS[skillId]; + const displayName = skillInfo?.displayName || skill?.name || skillId; + showNotification(`Learned ${displayName}!`, 'success'); } // Leaderboard @@ -11703,13 +11733,15 @@ playerStats.level = newLevel; playerStats.xp -= xpNeeded; - const classData = PLAYER_CLASSES[playerStats.class]; - playerStats.maxHp += classData.hpPerLevel; + const classData = PLAYER_CLASSES[playerStats.class] || { + hpPerLevel: 10, mpPerLevel: 5, atkPerLevel: 2, defPerLevel: 1 + }; + playerStats.maxHp += classData.hpPerLevel || 10; playerStats.hp = playerStats.maxHp; // Full heal on level up - playerStats.maxMp += classData.mpPerLevel; + playerStats.maxMp += classData.mpPerLevel || 5; playerStats.mp = playerStats.maxMp; // Full MP restore - playerStats.atk += classData.atkPerLevel; - playerStats.def += classData.defPerLevel; + playerStats.atk += classData.atkPerLevel || 2; + playerStats.def += classData.defPerLevel || 1; addCombatLog(`LEVEL UP! Now level ${newLevel}!`, 'victory'); @@ -11729,8 +11761,8 @@ // END RPG COMBAT SYSTEM FUNCTIONS // ========================================== - // Load monster types, skills, and spawn settings from database, then initialize auth - Promise.all([loadMonsterTypes(), loadSkillsFromDatabase(), loadSpawnSettings()]).then(() => { + // Load monster types, skills, classes, and spawn settings from database, then initialize auth + Promise.all([loadMonsterTypes(), loadSkillsFromDatabase(), loadClasses(), loadSpawnSettings()]).then(() => { loadCurrentUser(); });