398 lines
12 KiB
JavaScript
398 lines
12 KiB
JavaScript
/**
|
|
* 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;
|