/** * Galaxy Strike Online - Server Skill System * Manages skill progression, experience, and abilities */ class SkillSystem { constructor() { this.skills = new Map(); this.playerSkills = new Map(); // userId -> skill data this.initializeSkills(); } initializeSkills() { // Combat skills this.addSkill('weapons_mastery', { name: 'Weapons Mastery', description: 'Increases damage and accuracy with all weapons', category: 'combat', maxLevel: 100, experiencePerLevel: 1000, bonuses: { damage: 2, // +2% per level accuracy: 1, // +1% per level criticalChance: 0.5 // +0.5% per level } }); this.addSkill('defense_training', { name: 'Defense Training', description: 'Improves damage reduction and resistance', category: 'combat', maxLevel: 100, experiencePerLevel: 1000, bonuses: { damageReduction: 1.5, // +1.5% per level resistance: 1, // +1% per level health: 5 // +5 HP per level } }); this.addSkill('speed_reflexes', { name: 'Speed & Reflexes', description: 'Enhances movement speed and reaction time', category: 'combat', maxLevel: 100, experiencePerLevel: 1000, bonuses: { speed: 2, // +2% per level dodgeChance: 0.8, // +0.8% per level initiative: 1 // +1 per level } }); // Crafting skills this.addSkill('weapons_crafting', { name: 'Weapons Crafting', description: 'Ability to craft and improve weapons', category: 'crafting', maxLevel: 100, experiencePerLevel: 800, bonuses: { craftingSpeed: 2, // +2% per level craftingSuccess: 1, // +1% per level rareChance: 0.5 // +0.5% per level } }); this.addSkill('armor_crafting', { name: 'Armor Crafting', description: 'Ability to craft and improve armor', category: 'crafting', maxLevel: 100, experiencePerLevel: 800, bonuses: { craftingSpeed: 2, // +2% per level durabilityBonus: 3, // +3% per level rareChance: 0.5 // +0.5% per level } }); // Social skills this.addSkill('leadership', { name: 'Leadership', description: 'Improves team performance and guild bonuses', category: 'social', maxLevel: 100, experiencePerLevel: 1200, bonuses: { teamDamage: 1, // +1% per level guildBonus: 2, // +2% per level maxPartySize: 0.1 // +0.1 per level (rounded) } }); this.addSkill('negotiation', { name: 'Negotiation', description: 'Better prices from shops and traders', category: 'social', maxLevel: 100, experiencePerLevel: 1000, bonuses: { shopDiscount: 0.5, // -0.5% per level tradeBonus: 1, // +1% per level reputationGain: 2 // +2% per level } }); // Exploration skills this.addSkill('navigation', { name: 'Navigation', description: 'Faster travel and better map awareness', category: 'exploration', maxLevel: 100, experiencePerLevel: 900, bonuses: { travelSpeed: 3, // +3% per level discoveryRange: 2, // +2% per level mapAccuracy: 1 // +1% per level } }); this.addSkill('scavenging', { name: 'Scavenging', description: 'Find more materials and rare items', category: 'exploration', maxLevel: 100, experiencePerLevel: 900, bonuses: { materialFind: 2, // +2% per level rareFind: 1, // +1% per level salvageBonus: 3 // +3% per level } }); } addSkill(id, skill) { this.skills.set(id, { id, ...skill, createdAt: new Date().toISOString() }); } getSkill(id) { return this.skills.get(id); } getAllSkills() { return Array.from(this.skills.values()); } getSkillsByCategory(category) { return Array.from(this.skills.values()).filter(skill => skill.category === category); } initializePlayerData(userId) { if (!this.playerSkills.has(userId)) { this.playerSkills.set(userId, { skillPoints: 0, totalExperience: 0, skills: new Map(), unlockedSkills: new Set(['weapons_mastery', 'defense_training']), // Start with basic skills skillHistory: [] }); // Initialize unlocked skills at level 1 const playerData = this.playerSkills.get(userId); for (const skillId of playerData.unlockedSkills) { playerData.skills.set(skillId, { level: 1, experience: 0, totalExperience: 0, unlockedAt: new Date().toISOString() }); } } return this.playerSkills.get(userId); } getPlayerData(userId) { return this.playerSkills.get(userId) || this.initializePlayerData(userId); } addExperience(userId, skillId, amount) { const skill = this.getSkill(skillId); if (!skill) { throw new Error('Skill not found'); } const playerData = this.getPlayerData(userId); // Check if skill is unlocked if (!playerData.unlockedSkills.has(skillId)) { throw new Error('Skill not unlocked'); } const playerSkill = playerData.skills.get(skillId); if (!playerSkill) { throw new Error('Skill data not found'); } // Add experience playerSkill.experience += amount; playerSkill.totalExperience += amount; playerData.totalExperience += amount; let levelsGained = 0; let oldLevel = playerSkill.level; // Check for level up while (playerSkill.experience >= skill.experiencePerLevel && playerSkill.level < skill.maxLevel) { playerSkill.experience -= skill.experiencePerLevel; playerSkill.level += 1; levelsGained++; } // Record in history playerData.skillHistory.push({ skillId, experienceGained: amount, levelsGained, newLevel: playerSkill.level, timestamp: new Date().toISOString() }); // Keep only last 100 skill records if (playerData.skillHistory.length > 100) { playerData.skillHistory = playerData.skillHistory.slice(-100); } return { success: true, experienceGained: amount, levelsGained, newLevel: playerSkill.level, oldLevel, experienceToNext: playerSkill.level < skill.maxLevel ? skill.experiencePerLevel - playerSkill.experience : 0 }; } unlockSkill(userId, skillId) { const skill = this.getSkill(skillId); if (!skill) { throw new Error('Skill not found'); } const playerData = this.getPlayerData(userId); if (playerData.unlockedSkills.has(skillId)) { throw new Error('Skill already unlocked'); } // Add to unlocked skills playerData.unlockedSkills.add(skillId); // Initialize at level 1 playerData.skills.set(skillId, { level: 1, experience: 0, totalExperience: 0, unlockedAt: new Date().toISOString() }); return { success: true, skillId, skillName: skill.name, category: skill.category }; } getPlayerSkills(userId) { const playerData = this.getPlayerData(userId); const skills = []; for (const skillId of playerData.unlockedSkills) { const skill = this.getSkill(skillId); const playerSkill = playerData.skills.get(skillId); skills.push({ ...skill, level: playerSkill.level, experience: playerSkill.experience, totalExperience: playerSkill.totalExperience, experienceToNext: playerSkill.level < skill.maxLevel ? skill.experiencePerLevel - playerSkill.experience : 0, isMaxLevel: playerSkill.level >= skill.maxLevel, unlockedAt: playerSkill.unlockedAt }); } return skills; } getSkillBonuses(userId) { const playerData = this.getPlayerData(userId); const totalBonuses = { damage: 0, accuracy: 0, criticalChance: 0, damageReduction: 0, resistance: 0, health: 0, speed: 0, dodgeChance: 0, initiative: 0, craftingSpeed: 0, craftingSuccess: 0, rareChance: 0, durabilityBonus: 0, teamDamage: 0, guildBonus: 0, maxPartySize: 0, shopDiscount: 0, tradeBonus: 0, reputationGain: 0, travelSpeed: 0, discoveryRange: 0, mapAccuracy: 0, materialFind: 0, rareFind: 0, salvageBonus: 0 }; for (const skillId of playerData.unlockedSkills) { const skill = this.getSkill(skillId); const playerSkill = playerData.skills.get(skillId); if (skill && playerSkill) { for (const [bonus, value] of Object.entries(skill.bonuses)) { totalBonuses[bonus] += value * playerSkill.level; } } } return totalBonuses; } awardSkillPoints(userId, amount) { const playerData = this.getPlayerData(userId); playerData.skillPoints += amount; return { success: true, pointsAwarded: amount, totalPoints: playerData.skillPoints }; } allocateSkillPoint(userId, skillId) { const playerData = this.getPlayerData(userId); if (playerData.skillPoints <= 0) { throw new Error('No skill points available'); } const skill = this.getSkill(skillId); if (!skill) { throw new Error('Skill not found'); } const playerSkill = playerData.skills.get(skillId); if (!playerSkill) { throw new Error('Skill not unlocked'); } if (playerSkill.level >= skill.maxLevel) { throw new Error('Skill already at max level'); } // Allocate point playerSkill.level += 1; playerData.skillPoints -= 1; return { success: true, skillId, newLevel: playerSkill.level, remainingPoints: playerData.skillPoints }; } getSkillStatistics(userId) { const playerData = this.getPlayerData(userId); const skills = this.getPlayerSkills(userId); return { skillPoints: playerData.skillPoints, totalExperience: playerData.totalExperience, unlockedSkills: playerData.unlockedSkills.size, maxLevelSkills: skills.filter(skill => skill.isMaxLevel).length, averageLevel: skills.length > 0 ? Math.floor(skills.reduce((sum, skill) => sum + skill.level, 0) / skills.length) : 0, recentHistory: playerData.skillHistory.slice(-10) }; } } module.exports = SkillSystem;