/** * SKILL TREE SYSTEM * Player progression with farming, combat, and survival branches */ class SkillTreeSystem { constructor(scene) { this.scene = scene; this.enabled = true; // Player stats this.playerLevel = 1; this.playerXP = 0; this.skillPoints = 0; // Skill trees this.skills = { farming: new Map(), combat: new Map(), survival: new Map() }; // Active abilities this.activeAbilities = new Map(); this.abilityCooldowns = new Map(); // Settings this.settings = { xpPerLevel: 100, xpScaling: 1.5, // Each level requires 50% more XP skillPointsPerLevel: 1, maxLevel: 50 }; this.loadProgress(); this.init(); console.log('✅ Skill Tree System initialized'); } init() { this.defineSkills(); console.log('🌳 Skill trees ready'); } // ========== SKILL DEFINITIONS ========== defineSkills() { // FARMING TREE this.defineSkill('farming', 'green_thumb', { name: 'Green Thumb', description: 'Crops grow 10% faster', maxLevel: 5, cost: 1, requires: null, bonus: { type: 'crop_speed', value: 0.1 } }); this.defineSkill('farming', 'efficient_harvest', { name: 'Efficient Harvest', description: '+20% crop yield', maxLevel: 3, cost: 1, requires: 'green_thumb', bonus: { type: 'crop_yield', value: 0.2 } }); this.defineSkill('farming', 'area_harvest', { name: 'Area Harvest', description: 'Harvest 3x3 area at once', maxLevel: 1, cost: 2, requires: 'efficient_harvest', bonus: { type: 'ability', value: 'area_harvest' } }); this.defineSkill('farming', 'master_farmer', { name: 'Master Farmer', description: 'Crops never wither', maxLevel: 1, cost: 3, requires: 'area_harvest', bonus: { type: 'crop_immortal', value: true } }); // COMBAT TREE this.defineSkill('combat', 'strength', { name: 'Strength', description: '+15% melee damage', maxLevel: 5, cost: 1, requires: null, bonus: { type: 'melee_damage', value: 0.15 } }); this.defineSkill('combat', 'critical_strike', { name: 'Critical Strike', description: '10% chance for 2x damage', maxLevel: 3, cost: 1, requires: 'strength', bonus: { type: 'crit_chance', value: 0.1 } }); this.defineSkill('combat', 'dash', { name: 'Dash', description: 'Quick dash ability (cooldown: 5s)', maxLevel: 1, cost: 2, requires: 'strength', bonus: { type: 'ability', value: 'dash' } }); this.defineSkill('combat', 'berserker', { name: 'Berserker', description: '+50% damage when below 30% HP', maxLevel: 1, cost: 3, requires: 'critical_strike', bonus: { type: 'berserker', value: 0.5 } }); // SURVIVAL TREE this.defineSkill('survival', 'vitality', { name: 'Vitality', description: '+20 max health', maxLevel: 5, cost: 1, requires: null, bonus: { type: 'max_health', value: 20 } }); this.defineSkill('survival', 'regeneration', { name: 'Regeneration', description: 'Heal 1 HP every 5 seconds', maxLevel: 3, cost: 1, requires: 'vitality', bonus: { type: 'health_regen', value: 1 } }); this.defineSkill('survival', 'iron_skin', { name: 'Iron Skin', description: 'Reduce damage taken by 15%', maxLevel: 3, cost: 1, requires: 'vitality', bonus: { type: 'damage_reduction', value: 0.15 } }); this.defineSkill('survival', 'second_wind', { name: 'Second Wind', description: 'Survive fatal damage once (cooldown: 60s)', maxLevel: 1, cost: 3, requires: 'regeneration', bonus: { type: 'ability', value: 'second_wind' } }); } defineSkill(tree, id, data) { this.skills[tree].set(id, { id, tree, currentLevel: 0, ...data }); } // ========== SKILL MANAGEMENT ========== unlockSkill(tree, skillId) { const skill = this.skills[tree].get(skillId); if (!skill) return false; // Check requirements if (skill.requires) { const required = this.skills[tree].get(skill.requires); if (!required || required.currentLevel === 0) { console.log('❌ Requirement not met:', skill.requires); return false; } } // Check skill points if (this.skillPoints < skill.cost) { console.log('❌ Not enough skill points'); return false; } // Check max level if (skill.currentLevel >= skill.maxLevel) { console.log('❌ Skill already maxed'); return false; } // Unlock skill skill.currentLevel++; this.skillPoints -= skill.cost; // Apply bonus this.applySkillBonus(skill); // Unlock achievement if (this.scene.uiGraphics) { if (skill.currentLevel === skill.maxLevel) { this.scene.uiGraphics.unlockAchievement('skill_master'); } } this.saveProgress(); console.log(`✅ Unlocked: ${skill.name} (Level ${skill.currentLevel})`); return true; } applySkillBonus(skill) { const bonus = skill.bonus; switch (bonus.type) { case 'ability': this.activeAbilities.set(bonus.value, { name: skill.name, cooldown: 0, ready: true }); break; case 'max_health': if (this.scene.statsSystem) { this.scene.statsSystem.maxHealth += bonus.value; } break; case 'melee_damage': case 'crit_chance': case 'damage_reduction': case 'crop_speed': case 'crop_yield': // These are passive bonuses checked when needed break; } } // ========== EXPERIENCE & LEVELING ========== addXP(amount) { this.playerXP += amount; // Check for level up const xpNeeded = this.getXPForLevel(this.playerLevel + 1); if (this.playerXP >= xpNeeded && this.playerLevel < this.settings.maxLevel) { this.levelUp(); } this.saveProgress(); } getXPForLevel(level) { return Math.floor( this.settings.xpPerLevel * Math.pow(this.settings.xpScaling, level - 1) ); } levelUp() { this.playerLevel++; this.skillPoints += this.settings.skillPointsPerLevel; // Visual effect if (this.scene.visualEnhancements) { const player = this.scene.player; if (player) { const pos = player.getPosition(); this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y); } } // Notification if (this.scene.screenReader) { this.scene.screenReader.speak(`Level up! You are now level ${this.playerLevel}!`); } console.log(`🎉 Level Up! Now level ${this.playerLevel}`); this.saveProgress(); } // ========== ACTIVE ABILITIES ========== useAbility(abilityName) { const ability = this.activeAbilities.get(abilityName); if (!ability || !ability.ready) return false; switch (abilityName) { case 'dash': this.performDash(); this.setAbilityCooldown(abilityName, 5000); break; case 'area_harvest': this.performAreaHarvest(); this.setAbilityCooldown(abilityName, 10000); break; case 'second_wind': // Triggered automatically on fatal damage break; } return true; } performDash() { if (!this.scene.player) return; const player = this.scene.player; const direction = player.facing || 'down'; const distance = 3; let dx = 0, dy = 0; switch (direction) { case 'up': dy = -distance; break; case 'down': dy = distance; break; case 'left': dx = -distance; break; case 'right': dx = distance; break; } const pos = player.getPosition(); player.setPosition(pos.x + dx, pos.y + dy); // Dash effect if (this.scene.visualEnhancements) { this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y); } console.log('💨 Dash!'); } performAreaHarvest() { if (!this.scene.player || !this.scene.farmingSystem) return; const pos = this.scene.player.getPosition(); const x = Math.floor(pos.x); const y = Math.floor(pos.y); // Harvest 3x3 area let harvested = 0; for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { if (this.scene.farmingSystem.harvestCrop(x + dx, y + dy)) { harvested++; } } } if (harvested > 0) { console.log(`🌾 Area harvest: ${harvested} crops`); } } setAbilityCooldown(abilityName, duration) { const ability = this.activeAbilities.get(abilityName); if (!ability) return; ability.ready = false; ability.cooldown = duration; setTimeout(() => { ability.ready = true; ability.cooldown = 0; }, duration); } // ========== SKILL BONUSES ========== getSkillBonus(bonusType) { let total = 0; for (const tree of Object.values(this.skills)) { for (const skill of tree.values()) { if (skill.currentLevel > 0 && skill.bonus.type === bonusType) { total += skill.bonus.value * skill.currentLevel; } } } return total; } hasSkill(tree, skillId) { const skill = this.skills[tree].get(skillId); return skill && skill.currentLevel > 0; } // ========== SKILL RESET ========== resetSkills(cost = 1000) { // Check if player has enough gold if (this.scene.inventorySystem && this.scene.inventorySystem.gold < cost) { console.log('❌ Not enough gold to reset skills'); return false; } // Deduct cost if (this.scene.inventorySystem) { this.scene.inventorySystem.gold -= cost; } // Reset all skills let pointsRefunded = 0; for (const tree of Object.values(this.skills)) { for (const skill of tree.values()) { pointsRefunded += skill.currentLevel * skill.cost; skill.currentLevel = 0; } } this.skillPoints = pointsRefunded; this.activeAbilities.clear(); this.saveProgress(); console.log(`🔄 Skills reset! ${pointsRefunded} points refunded`); return true; } // ========== PERSISTENCE ========== saveProgress() { const data = { level: this.playerLevel, xp: this.playerXP, skillPoints: this.skillPoints, skills: {} }; // Save skill levels for (const [treeName, tree] of Object.entries(this.skills)) { data.skills[treeName] = {}; for (const [skillId, skill] of tree.entries()) { if (skill.currentLevel > 0) { data.skills[treeName][skillId] = skill.currentLevel; } } } localStorage.setItem('novafarma_skill_tree', JSON.stringify(data)); } loadProgress() { const saved = localStorage.getItem('novafarma_skill_tree'); if (saved) { try { const data = JSON.parse(saved); this.playerLevel = data.level || 1; this.playerXP = data.xp || 0; this.skillPoints = data.skillPoints || 0; // Load skill levels after skills are defined this.savedSkills = data.skills || {}; console.log('✅ Skill progress loaded'); } catch (error) { console.error('Failed to load skill progress:', error); } } } applyLoadedProgress() { if (!this.savedSkills) return; for (const [treeName, skills] of Object.entries(this.savedSkills)) { for (const [skillId, level] of Object.entries(skills)) { const skill = this.skills[treeName]?.get(skillId); if (skill) { skill.currentLevel = level; this.applySkillBonus(skill); } } } this.savedSkills = null; } destroy() { this.saveProgress(); console.log('🌳 Skill Tree System destroyed'); } }