fixed server software

This commit is contained in:
Robert MacRae 2026-01-24 21:39:56 -04:00
parent a166f27d23
commit 9df6b22721
16 changed files with 4831 additions and 7 deletions

View File

@ -1,17 +1,18 @@
# Game Server Configuration
PORT=3001
PORT=3002
NODE_ENV=production
# Database Configuration
MONGODB_URI=mongodb://localhost:27017/galaxystrikeonline
# Optional: API Server URL for authentication validation
# API Server Configuration
API_SERVER_URL=http://localhost:3000
GAME_SERVER_URL=https://dev.gameserver.galaxystrike.online
# Optional: Server identification
SERVER_NAME=Game Server
SERVER_REGION=us-east
MAX_PLAYERS=50
SERVER_NAME=Dev Game Server
SERVER_REGION=local
MAX_PLAYERS=8
# Logging
LOG_LEVEL=info

243
GameServer/routes/base.js Normal file
View File

@ -0,0 +1,243 @@
const express = require('express');
const BaseSystem = require('../systems/BaseSystem');
const router = express.Router();
// Initialize base system
const baseSystem = new BaseSystem();
// Get player base
router.get('/player/:userId', async (req, res) => {
try {
const { userId } = req.params;
const base = baseSystem.getPlayerBase(userId);
res.json({
success: true,
base
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get base statistics
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = baseSystem.getBaseStatistics(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Add experience to base
router.post('/experience/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { amount } = req.body;
if (!amount || amount <= 0) {
return res.status(400).json({
success: false,
error: 'Valid experience amount required'
});
}
const result = baseSystem.addExperience(userId, amount);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Build room
router.post('/build/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { roomType, level = 1 } = req.body;
if (!roomType) {
return res.status(400).json({
success: false,
error: 'Room type required'
});
}
const result = baseSystem.buildRoom(userId, roomType, level);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Upgrade room
router.post('/upgrade/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { roomType } = req.body;
if (!roomType) {
return res.status(400).json({
success: false,
error: 'Room type required'
});
}
const result = baseSystem.upgradeRoom(userId, roomType);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Demolish room
router.post('/demolish/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { roomType } = req.body;
if (!roomType) {
return res.status(400).json({
success: false,
error: 'Room type required'
});
}
const result = baseSystem.demolishRoom(userId, roomType);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Add decoration
router.post('/decoration/add/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { decorationType } = req.body;
if (!decorationType) {
return res.status(400).json({
success: false,
error: 'Decoration type required'
});
}
const result = baseSystem.addDecoration(userId, decorationType);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Remove decoration
router.post('/decoration/remove/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { decorationIndex } = req.body;
if (decorationIndex === undefined || decorationIndex === null) {
return res.status(400).json({
success: false,
error: 'Decoration index required'
});
}
const result = baseSystem.removeDecoration(userId, decorationIndex);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get available rooms
router.get('/rooms/available/:userId', async (req, res) => {
try {
const { userId } = req.params;
const rooms = baseSystem.getAvailableRooms(userId);
res.json({
success: true,
rooms
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get room upgrades
router.get('/rooms/upgrades/:userId', async (req, res) => {
try {
const { userId } = req.params;
const upgrades = baseSystem.getRoomUpgrades(userId);
res.json({
success: true,
upgrades
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

View File

@ -0,0 +1,212 @@
const express = require('express');
const CraftingSystem = require('../systems/CraftingSystem');
const router = express.Router();
// Initialize crafting system
const craftingSystem = new CraftingSystem();
// Get all recipes
router.get('/recipes', async (req, res) => {
try {
const recipes = craftingSystem.getAllRecipes();
res.json({
success: true,
recipes
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get recipe by ID
router.get('/recipes/:id', async (req, res) => {
try {
const { id } = req.params;
const recipe = craftingSystem.getRecipe(id);
if (!recipe) {
return res.status(404).json({
success: false,
error: 'Recipe not found'
});
}
res.json({
success: true,
recipe
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get recipes by type
router.get('/recipes/type/:type', async (req, res) => {
try {
const { type } = req.params;
const recipes = craftingSystem.getRecipesByType(type);
res.json({
success: true,
recipes
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player crafting data
router.get('/player/:userId', async (req, res) => {
try {
const { userId } = req.params;
const playerData = craftingSystem.getPlayerData(userId);
res.json({
success: true,
playerData
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Check if can craft
router.post('/can-craft/:userId/:recipeId', async (req, res) => {
try {
const { userId, recipeId } = req.params;
const { inventory } = req.body;
if (!inventory) {
return res.status(400).json({
success: false,
error: 'Inventory data required'
});
}
const canCraft = craftingSystem.canCraft(userId, recipeId, inventory);
res.json({
success: true,
canCraft
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Craft item
router.post('/craft/:userId/:recipeId', async (req, res) => {
try {
const { userId, recipeId } = req.params;
const { inventory } = req.body;
if (!inventory) {
return res.status(400).json({
success: false,
error: 'Inventory data required'
});
}
const result = await craftingSystem.craftItem(userId, recipeId, inventory);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Learn recipe
router.post('/learn/:userId/:recipeId', async (req, res) => {
try {
const { userId, recipeId } = req.params;
const result = craftingSystem.learnRecipe(userId, recipeId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player known recipes
router.get('/known/:userId', async (req, res) => {
try {
const { userId } = req.params;
const recipes = craftingSystem.getPlayerKnownRecipes(userId);
res.json({
success: true,
recipes
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get crafting stats
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = craftingSystem.getCraftingStats(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Discover recipe
router.post('/discover/:userId/:recipeId', async (req, res) => {
try {
const { userId, recipeId } = req.params;
const result = craftingSystem.discoverRecipe(userId, recipeId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

View File

@ -0,0 +1,266 @@
const express = require('express');
const DungeonSystem = require('../systems/DungeonSystem');
const router = express.Router();
// Initialize dungeon system
const dungeonSystem = new DungeonSystem();
// Get all dungeons
router.get('/', async (req, res) => {
try {
const dungeons = dungeonSystem.getAllDungeons();
res.json({
success: true,
dungeons
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get dungeons by difficulty
router.get('/difficulty/:difficulty', async (req, res) => {
try {
const { difficulty } = req.params;
const dungeons = dungeonSystem.getDungeonsByDifficulty(difficulty);
res.json({
success: true,
dungeons
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get available dungeons for player
router.get('/available/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { playerLevel = 1, playerCount = 1 } = req.query;
const dungeons = dungeonSystem.getAvailableDungeons(parseInt(playerLevel), parseInt(playerCount));
res.json({
success: true,
dungeons
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Create dungeon instance
router.post('/create/:dungeonId/:creatorId', async (req, res) => {
try {
const { dungeonId, creatorId } = req.params;
const { playerIds = [] } = req.body;
const instance = dungeonSystem.createInstance(dungeonId, creatorId, playerIds);
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get dungeon instance
router.get('/instance/:instanceId', async (req, res) => {
try {
const { instanceId } = req.params;
const instance = dungeonSystem.getInstance(instanceId);
if (!instance) {
return res.status(404).json({
success: false,
error: 'Instance not found'
});
}
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player's current instance
router.get('/player/:userId', async (req, res) => {
try {
const { userId } = req.params;
const instance = dungeonSystem.getPlayerInstance(userId);
if (!instance) {
return res.status(404).json({
success: false,
error: 'No active instance found'
});
}
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Join dungeon instance
router.post('/join/:instanceId/:userId', async (req, res) => {
try {
const { instanceId, userId } = req.params;
const instance = dungeonSystem.joinInstance(instanceId, userId);
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Leave dungeon instance
router.post('/leave/:userId', async (req, res) => {
try {
const { userId } = req.params;
const instance = dungeonSystem.leaveInstance(userId);
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Start encounter
router.post('/encounter/start/:instanceId/:userId', async (req, res) => {
try {
const { instanceId, userId } = req.params;
const result = dungeonSystem.startEncounter(instanceId, userId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Complete encounter
router.post('/encounter/complete/:instanceId/:userId', async (req, res) => {
try {
const { instanceId, userId } = req.params;
const { result } = req.body;
if (!result) {
return res.status(400).json({
success: false,
error: 'Encounter result required'
});
}
const encounterResult = dungeonSystem.completeEncounter(instanceId, userId, result);
res.json({
success: true,
result: encounterResult
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Complete dungeon
router.post('/complete/:instanceId', async (req, res) => {
try {
const { instanceId } = req.params;
const result = dungeonSystem.completeDungeon(instanceId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get dungeon statistics
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = dungeonSystem.getDungeonStatistics(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Cleanup expired instances
router.post('/cleanup', async (req, res) => {
try {
const cleanedCount = dungeonSystem.cleanupExpiredInstances();
res.json({
success: true,
cleanedCount
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

282
GameServer/routes/idle.js Normal file
View File

@ -0,0 +1,282 @@
const express = require('express');
const IdleSystem = require('../systems/IdleSystem');
const router = express.Router();
// Initialize idle system
const idleSystem = new IdleSystem();
// Calculate offline progress
router.post('/progress/:userId', async (req, res) => {
try {
const { userId } = req.params;
const progress = idleSystem.calculateOfflineProgress(userId);
res.json({
success: true,
progress: {
offlineTime: progress.offlineTime,
offlineSeconds: progress.offlineSeconds,
rewards: progress.rewards,
productionRates: progress.productionRates
}
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Apply idle rewards to player data
router.post('/rewards/apply/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { playerData } = req.body;
if (!playerData) {
return res.status(400).json({
success: false,
error: 'Player data required'
});
}
const progress = idleSystem.applyIdleRewards(userId, playerData);
res.json({
success: true,
progress,
updatedPlayerData: playerData
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get production rates
router.get('/production/:userId', async (req, res) => {
try {
const { userId } = req.params;
const productionRates = idleSystem.getProductionRates(userId);
res.json({
success: true,
productionRates
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Update production rates
router.post('/production/update/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { rates } = req.body;
if (!rates) {
return res.status(400).json({
success: false,
error: 'Production rates required'
});
}
idleSystem.updateProductionRates(userId, rates);
const updatedRates = idleSystem.getProductionRates(userId);
res.json({
success: true,
productionRates: updatedRates
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Update bonuses
router.post('/bonuses/update/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { bonuses } = req.body;
if (!bonuses) {
return res.status(400).json({
success: false,
error: 'Bonuses required'
});
}
const updatedRates = idleSystem.updateBonuses(userId, bonuses);
res.json({
success: true,
productionRates: updatedRates
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get achievements
router.get('/achievements/:userId', async (req, res) => {
try {
const { userId } = req.params;
const achievements = idleSystem.getAchievements(userId);
res.json({
success: true,
achievements
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get idle stats
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = idleSystem.getIdleStats(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Calculate income for specific time
router.post('/income/calculate/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { timeInSeconds } = req.body;
if (!timeInSeconds || timeInSeconds <= 0) {
return res.status(400).json({
success: false,
error: 'Valid time in seconds required'
});
}
const income = idleSystem.calculateIncomeForTime(userId, timeInSeconds);
res.json({
success: true,
timeInSeconds,
income
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get time until milestone
router.get('/milestone/:userId', async (req, res) => {
try {
const { userId } = req.params;
const targetCredits = parseInt(req.query.target) || 10000;
const timeUntil = idleSystem.getTimeUntilMilestone(userId, targetCredits);
res.json({
success: true,
targetCredits,
timeUntil
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Update last active time
router.post('/active/:userId', async (req, res) => {
try {
const { userId } = req.params;
idleSystem.updateLastActive(userId);
res.json({
success: true,
lastActive: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get global idle statistics
router.get('/global', async (req, res) => {
try {
const globalStats = idleSystem.getGlobalStats();
res.json({
success: true,
globalStats
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Reset player data (admin only)
router.post('/reset/:userId', async (req, res) => {
try {
const { userId } = req.params;
idleSystem.resetPlayerData(userId);
res.json({
success: true,
message: 'Player idle data reset successfully'
});
} catch (error) {
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
module.exports = router;

232
GameServer/routes/quests.js Normal file
View File

@ -0,0 +1,232 @@
const express = require('express');
const QuestSystem = require('../systems/QuestSystem');
const router = express.Router();
// Initialize quest system
const questSystem = new QuestSystem();
// Get all quests
router.get('/', async (req, res) => {
try {
const quests = questSystem.getAllQuests();
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get quests by type
router.get('/type/:type', async (req, res) => {
try {
const { type } = req.params;
const quests = questSystem.getQuestsByType(type);
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get quests by difficulty
router.get('/difficulty/:difficulty', async (req, res) => {
try {
const { difficulty } = req.params;
const quests = questSystem.getQuestsByDifficulty(difficulty);
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get available quests for player
router.get('/available/:userId', async (req, res) => {
try {
const { userId } = req.params;
const quests = questSystem.getAvailableQuests(userId);
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Start quest
router.post('/start/:userId/:questId', async (req, res) => {
try {
const { userId, questId } = req.params;
const result = questSystem.startQuest(userId, questId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Update quest progress
router.post('/progress/:userId/:questId', async (req, res) => {
try {
const { userId, questId } = req.params;
const { progressUpdates } = req.body;
if (!progressUpdates) {
return res.status(400).json({
success: false,
error: 'Progress updates required'
});
}
const result = questSystem.updateQuestProgress(userId, questId, progressUpdates);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Complete quest
router.post('/complete/:userId/:questId', async (req, res) => {
try {
const { userId, questId } = req.params;
const result = questSystem.completeQuest(userId, questId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player active quests
router.get('/active/:userId', async (req, res) => {
try {
const { userId } = req.params;
const quests = questSystem.getPlayerActiveQuests(userId);
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player completed quests
router.get('/completed/:userId', async (req, res) => {
try {
const { userId } = req.params;
const quests = questSystem.getPlayerCompletedQuests(userId);
res.json({
success: true,
quests
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get quest statistics
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = questSystem.getQuestStatistics(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Reset daily quests
router.post('/reset/daily/:userId', async (req, res) => {
try {
const { userId } = req.params;
const result = questSystem.resetDailyQuests(userId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Reset weekly quests
router.post('/reset/weekly/:userId', async (req, res) => {
try {
const { userId } = req.params;
const result = questSystem.resetWeeklyQuests(userId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

452
GameServer/routes/ships.js Normal file
View File

@ -0,0 +1,452 @@
const express = require('express');
const ShipSystem = require('../systems/ShipSystem');
const router = express.Router();
// Initialize ship system
const shipSystem = new ShipSystem();
// Get all ship templates
router.get('/templates', async (req, res) => {
try {
const templates = shipSystem.getAllShipTemplates();
res.json({
success: true,
templates
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get ship templates by class
router.get('/templates/class/:shipClass', async (req, res) => {
try {
const { shipClass } = req.params;
const templates = shipSystem.getShipTemplatesByClass(shipClass);
res.json({
success: true,
templates
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get ship templates by rarity
router.get('/templates/rarity/:rarity', async (req, res) => {
try {
const { rarity } = req.params;
const templates = shipSystem.getShipTemplatesByRarity(rarity);
res.json({
success: true,
templates
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get available ships for player
router.get('/available/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { playerLevel = 1 } = req.query;
const ships = shipSystem.getAvailableShips(userId, parseInt(playerLevel));
res.json({
success: true,
ships
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player ships
router.get('/player/:userId', async (req, res) => {
try {
const { userId } = req.params;
const ships = shipSystem.getPlayerShips(userId);
res.json({
success: true,
ships: ships.map(ship => ({
id: ship.id,
name: ship.name,
class: ship.class,
rarity: ship.rarity,
level: ship.level,
experience: ship.experience,
requiredExp: ship.requiredExp,
stats: ship.stats,
maxStats: ship.maxStats,
slots: ship.slots,
equippedItems: ship.equippedItems,
texture: ship.texture,
status: ship.status,
createdAt: ship.createdAt,
lastUsed: ship.lastUsed
}))
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get specific ship
router.get('/player/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const ship = shipSystem.getShip(userId, shipId);
if (!ship) {
return res.status(404).json({
success: false,
error: 'Ship not found'
});
}
res.json({
success: true,
ship: {
id: ship.id,
name: ship.name,
class: ship.class,
rarity: ship.rarity,
level: ship.level,
experience: ship.experience,
requiredExp: ship.requiredExp,
stats: ship.stats,
maxStats: ship.maxStats,
slots: ship.slots,
equippedItems: ship.equippedItems,
texture: ship.texture,
status: ship.status,
createdAt: ship.createdAt,
lastUsed: ship.lastUsed
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get current ship
router.get('/current/:userId', async (req, res) => {
try {
const { userId } = req.params;
const currentShip = shipSystem.getCurrentShip(userId);
if (!currentShip) {
return res.status(404).json({
success: false,
error: 'No current ship found'
});
}
res.json({
success: true,
ship: {
id: currentShip.id,
name: currentShip.name,
class: currentShip.class,
rarity: currentShip.rarity,
level: currentShip.level,
experience: currentShip.experience,
requiredExp: currentShip.requiredExp,
stats: currentShip.stats,
maxStats: currentShip.maxStats,
slots: currentShip.slots,
equippedItems: currentShip.equippedItems,
texture: currentShip.texture,
status: currentShip.status
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Create new ship
router.post('/create/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { templateId, name, level = 1 } = req.body;
if (!templateId) {
return res.status(400).json({
success: false,
error: 'Ship template ID required'
});
}
const ship = shipSystem.createShip(userId, templateId, { name, level });
const playerShips = shipSystem.getPlayerShips(userId);
playerShips.push(ship);
res.json({
success: true,
ship: {
id: ship.id,
name: ship.name,
class: ship.class,
rarity: ship.rarity,
level: ship.level,
experience: ship.experience,
requiredExp: ship.requiredExp,
stats: ship.stats,
maxStats: ship.maxStats,
slots: ship.slots,
equippedItems: ship.equippedItems,
texture: ship.texture,
status: ship.status,
createdAt: ship.createdAt
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Set current ship
router.post('/current/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const ship = shipSystem.setCurrentShip(userId, shipId);
res.json({
success: true,
ship: {
id: ship.id,
name: ship.name,
class: ship.class,
level: ship.level,
stats: ship.stats,
status: ship.status,
lastUsed: ship.lastUsed
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Add experience to ship
router.post('/experience/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const { amount } = req.body;
if (!amount || amount <= 0) {
return res.status(400).json({
success: false,
error: 'Valid experience amount required'
});
}
const result = shipSystem.addExperience(userId, shipId, amount);
res.json({
success: true,
result: {
experienceGained: result.experienceGained,
levelsGained: result.levelsGained,
newLevel: result.newLevel,
newStats: result.newStats
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Upgrade ship
router.post('/upgrade/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const { upgradeType } = req.body;
if (!upgradeType) {
return res.status(400).json({
success: false,
error: 'Upgrade type required'
});
}
const result = shipSystem.upgradeShip(userId, shipId, upgradeType);
res.json({
success: true,
result: {
upgradeType: result.upgradeType,
newStats: result.newStats
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Equip item to ship
router.post('/equip/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const { slot, itemId } = req.body;
if (!slot || !itemId) {
return res.status(400).json({
success: false,
error: 'Slot and item ID required'
});
}
const result = shipSystem.equipItem(userId, shipId, slot, itemId);
res.json({
success: true,
result: {
slot: result.slot,
itemId: result.itemId,
equippedItems: result.equippedItems
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Unequip item from ship
router.post('/unequip/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const { slot } = req.body;
if (!slot) {
return res.status(400).json({
success: false,
error: 'Slot required'
});
}
const result = shipSystem.unequipItem(userId, shipId, slot);
res.json({
success: true,
result: {
slot: result.slot,
itemId: result.itemId,
equippedItems: result.equippedItems
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Repair ship
router.post('/repair/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const { amount } = req.body;
const result = shipSystem.repairShip(userId, shipId, amount);
res.json({
success: true,
result: {
health: result.health,
maxHealth: result.maxHealth,
repaired: result.repaired
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Delete ship
router.delete('/:userId/:shipId', async (req, res) => {
try {
const { userId, shipId } = req.params;
const deletedShip = shipSystem.deleteShip(userId, shipId);
res.json({
success: true,
deletedShip: {
id: deletedShip.id,
name: deletedShip.name,
class: deletedShip.class
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get ship statistics
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = shipSystem.getPlayerShipStats(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

187
GameServer/routes/skills.js Normal file
View File

@ -0,0 +1,187 @@
const express = require('express');
const SkillSystem = require('../systems/SkillSystem');
const router = express.Router();
// Initialize skill system
const skillSystem = new SkillSystem();
// Get all skills
router.get('/', async (req, res) => {
try {
const skills = skillSystem.getAllSkills();
res.json({
success: true,
skills
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get skills by category
router.get('/category/:category', async (req, res) => {
try {
const { category } = req.params;
const skills = skillSystem.getSkillsByCategory(category);
res.json({
success: true,
skills
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player skills
router.get('/player/:userId', async (req, res) => {
try {
const { userId } = req.params;
const skills = skillSystem.getPlayerSkills(userId);
res.json({
success: true,
skills
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Add experience to skill
router.post('/experience/:userId/:skillId', async (req, res) => {
try {
const { userId, skillId } = req.params;
const { amount } = req.body;
if (!amount || amount <= 0) {
return res.status(400).json({
success: false,
error: 'Valid experience amount required'
});
}
const result = skillSystem.addExperience(userId, skillId, amount);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Unlock skill
router.post('/unlock/:userId/:skillId', async (req, res) => {
try {
const { userId, skillId } = req.params;
const result = skillSystem.unlockSkill(userId, skillId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get skill bonuses
router.get('/bonuses/:userId', async (req, res) => {
try {
const { userId } = req.params;
const bonuses = skillSystem.getSkillBonuses(userId);
res.json({
success: true,
bonuses
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Award skill points
router.post('/points/award/:userId', async (req, res) => {
try {
const { userId } = req.params;
const { amount } = req.body;
if (!amount || amount <= 0) {
return res.status(400).json({
success: false,
error: 'Valid point amount required'
});
}
const result = skillSystem.awardSkillPoints(userId, amount);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Allocate skill point
router.post('/points/allocate/:userId/:skillId', async (req, res) => {
try {
const { userId, skillId } = req.params;
const result = skillSystem.allocateSkillPoint(userId, skillId);
res.json({
success: true,
result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get skill statistics
router.get('/stats/:userId', async (req, res) => {
try {
const { userId } = req.params;
const stats = skillSystem.getSkillStatistics(userId);
res.json({
success: true,
stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

View File

@ -99,6 +99,15 @@ app.get('/api/game/player/:id', (req, res) => {
}
});
// Game system routes
app.use('/api/crafting', require('./routes/crafting'));
app.use('/api/quests', require('./routes/quests'));
app.use('/api/skills', require('./routes/skills'));
app.use('/api/ships', require('./routes/ships'));
app.use('/api/idle', require('./routes/idle'));
app.use('/api/dungeons', require('./routes/dungeons'));
app.use('/api/base', require('./routes/base'));
// Socket.IO handlers (based on LocalServer)
io.on('connection', (socket) => {
console.log('[GAME SERVER] Client connected:', socket.id);
@ -251,16 +260,19 @@ io.on('connection', (socket) => {
});
// Start server
const PORT = process.env.PORT || 3001;
const PORT = process.env.PORT || 3002;
async function startServer() {
try {
// Connect to database
await connectDB();
server.listen(PORT, () => {
server.listen(PORT, async () => {
console.log(`[GAME SERVER] Game server running on port ${PORT}`);
console.log(`[GAME SERVER] Socket.IO ready for multiplayer connections`);
// Register with API server
await registerWithAPIServer();
});
} catch (error) {
console.error('[GAME SERVER] Failed to start server:', error);
@ -268,6 +280,51 @@ async function startServer() {
}
}
// Register this GameServer with the API server
async function registerWithAPIServer() {
try {
// Force use of local API server for development
const apiServerUrl = 'http://localhost:3001';
const gameServerUrl = 'https://dev.gameserver.galaxystrike.online';
const serverData = {
serverId: `devgame-server-${PORT}`,
name: 'Dev Game Server',
type: 'public', // Must be 'public' or 'private' according to model
region: 'Europe',
maxPlayers: 100, // Hardcoded to allow 100 players
currentPlayers: 0,
gameServerUrl: gameServerUrl,
owner: {
userId: 'developer',
username: 'Developer'
}
};
console.log(`[GAME SERVER] Registering with API server at ${apiServerUrl}`);
console.log(`[GAME SERVER] Server data:`, serverData);
const response = await fetch(`${apiServerUrl}/api/servers/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(serverData)
});
if (response.ok) {
const result = await response.json();
console.log(`[GAME SERVER] Successfully registered with API server:`, result);
} else {
const errorText = await response.text();
console.error(`[GAME SERVER] Failed to register with API server: ${response.status}`);
console.error(`[GAME SERVER] Error response:`, errorText);
}
} catch (error) {
console.error('[GAME SERVER] Error registering with API server:', error);
}
}
startServer();
module.exports = { app, server, io, gameInstances, connectedClients };

View File

@ -0,0 +1,512 @@
/**
* Galaxy Strike Online - Server Base System
* Manages player base building and customization
*/
class BaseSystem {
constructor() {
this.playerBases = new Map(); // userId -> base data
// Room types
this.roomTypes = {
command_center: {
name: 'Command Center',
description: 'Central control room for your operations',
size: 'large',
powerCost: 20,
storageBonus: 0,
buildCost: 0,
requiredLevel: 1,
maxLevel: 1,
icon: 'fa-satellite-dish'
},
barracks: {
name: 'Barracks',
description: 'Housing for crew and allies',
size: 'medium',
powerCost: 10,
storageBonus: 100,
buildCost: 500,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-home'
},
laboratory: {
name: 'Laboratory',
description: 'Research facility for new technologies',
size: 'medium',
powerCost: 15,
storageBonus: 50,
buildCost: 1000,
requiredLevel: 3,
maxLevel: 5,
icon: 'fa-flask'
},
workshop: {
name: 'Workshop',
description: 'Crafting and equipment modification station',
size: 'medium',
powerCost: 12,
storageBonus: 80,
buildCost: 800,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-hammer'
},
storage_bay: {
name: 'Storage Bay',
description: 'Additional storage for resources and items',
size: 'large',
powerCost: 5,
storageBonus: 500,
buildCost: 300,
requiredLevel: 1,
maxLevel: 10,
icon: 'fa-warehouse'
},
power_generator: {
name: 'Power Generator',
description: 'Generates power for base operations',
size: 'small',
powerCost: -25, // Generates power
storageBonus: 0,
buildCost: 400,
requiredLevel: 2,
maxLevel: 5,
icon: 'fa-bolt'
},
defense_turret: {
name: 'Defense Turret',
description: 'Automated defense system',
size: 'small',
powerCost: 8,
storageBonus: 0,
buildCost: 600,
requiredLevel: 4,
maxLevel: 8,
icon: 'fa-crosshairs'
},
medical_bay: {
name: 'Medical Bay',
description: 'Healing station for crew',
size: 'medium',
powerCost: 10,
storageBonus: 30,
buildCost: 750,
requiredLevel: 3,
maxLevel: 5,
icon: 'fa-heartbeat'
},
communication_array: {
name: 'Communication Array',
description: 'Enhances communication and scanning',
size: 'large',
powerCost: 15,
storageBonus: 20,
buildCost: 1200,
requiredLevel: 5,
maxLevel: 3,
icon: 'fa-broadcast-tower'
},
refinery: {
name: 'Refinery',
description: 'Processes raw materials into refined resources',
size: 'large',
powerCost: 20,
storageBonus: 100,
buildCost: 2000,
requiredLevel: 6,
maxLevel: 5,
icon: 'fa-industry'
}
};
// Decoration types
this.decorationTypes = {
trophy_case: {
name: 'Trophy Case',
description: 'Display your achievements',
buildCost: 100,
icon: 'fa-trophy'
},
statue: {
name: 'Hero Statue',
description: 'Commemorative statue',
buildCost: 500,
icon: 'fa-monument'
},
garden: {
name: 'Garden',
description: 'Decorative garden area',
buildCost: 200,
icon: 'fa-tree'
},
fountain: {
name: 'Fountain',
description: 'Decorative water feature',
buildCost: 300,
icon: 'fa-water'
},
banner: {
name: 'Banner',
description: 'Display your faction colors',
buildCost: 150,
icon: 'fa-flag'
}
};
}
initializePlayerBase(userId) {
if (!this.playerBases.has(userId)) {
this.playerBases.set(userId, {
name: 'Command Center Alpha',
level: 1,
experience: 0,
experienceToNext: 500,
rooms: [
{
type: 'command_center',
level: 1,
builtAt: new Date().toISOString(),
lastUpgraded: null
}
],
decorations: [],
power: 100,
maxPower: 100,
storage: 1000,
maxStorage: 1000,
defense: 10,
maxDefense: 10,
crewCapacity: 5,
maxCrewCapacity: 5,
researchBonus: 0,
craftingBonus: 0,
createdAt: new Date().toISOString(),
lastActive: new Date().toISOString()
});
}
return this.playerBases.get(userId);
}
getPlayerBase(userId) {
return this.playerBases.get(userId) || this.initializePlayerBase(userId);
}
addExperience(userId, amount) {
const base = this.getPlayerBase(userId);
base.experience += amount;
let levelsGained = 0;
const oldLevel = base.level;
// Check for level up
while (base.experience >= base.experienceToNext) {
base.experience -= base.experienceToNext;
base.level += 1;
levelsGained++;
// Update experience needed for next level
base.experienceToNext = this.getExperienceNeeded(base.level);
// Apply level up bonuses
this.applyLevelUpBonuses(base);
}
base.lastActive = new Date().toISOString();
return {
experienceGained: amount,
levelsGained,
newLevel: base.level,
experienceToNext: base.experienceToNext
};
}
getExperienceNeeded(level) {
// Exponential experience curve
return Math.floor(500 * Math.pow(1.5, level - 1));
}
applyLevelUpBonuses(base) {
// Increase base stats on level up
base.maxPower += 20;
base.maxStorage += 200;
base.maxDefense += 5;
base.maxCrewCapacity += 2;
// Restore to max values
base.power = Math.min(base.power, base.maxPower);
base.storage = Math.min(base.storage, base.maxStorage);
base.defense = Math.min(base.defense, base.maxDefense);
base.crewCapacity = Math.min(base.crewCapacity, base.maxCrewCapacity);
}
buildRoom(userId, roomType, level = 1) {
const base = this.getPlayerBase(userId);
const roomTemplate = this.roomTypes[roomType];
if (!roomTemplate) {
throw new Error('Invalid room type');
}
// Check requirements
if (base.level < roomTemplate.requiredLevel) {
throw new Error('Base level too low');
}
// Check if room already exists
const existingRoom = base.rooms.find(room => room.type === roomType);
if (existingRoom) {
throw new Error('Room already exists');
}
// Check power capacity
const totalPowerCost = this.calculateTotalPowerCost(base);
if (totalPowerCost + roomTemplate.powerCost > base.maxPower) {
throw new Error('Insufficient power capacity');
}
// Add room
const room = {
type: roomType,
level,
builtAt: new Date().toISOString(),
lastUpgraded: null
};
base.rooms.push(room);
this.updateBaseStats(base);
return {
success: true,
room,
newStats: {
power: base.power,
maxPower: base.maxPower,
storage: base.storage,
maxStorage: base.maxStorage,
defense: base.defense,
maxDefense: base.maxDefense
}
};
}
upgradeRoom(userId, roomType) {
const base = this.getPlayerBase(userId);
const roomTemplate = this.roomTypes[roomType];
const room = base.rooms.find(room => room.type === roomType);
if (!room) {
throw new Error('Room not found');
}
if (room.level >= roomTemplate.maxLevel) {
throw new Error('Room already at max level');
}
// Upgrade room
room.level += 1;
room.lastUpgraded = new Date().toISOString();
this.updateBaseStats(base);
return {
success: true,
room,
newStats: {
power: base.power,
maxPower: base.maxPower,
storage: base.storage,
maxStorage: base.maxStorage,
defense: base.defense,
maxDefense: base.maxDefense
}
};
}
demolishRoom(userId, roomType) {
const base = this.getPlayerBase(userId);
const roomIndex = base.rooms.findIndex(room => room.type === roomType);
if (roomIndex === -1) {
throw new Error('Room not found');
}
const room = base.rooms[roomIndex];
// Cannot demolish command center
if (room.type === 'command_center') {
throw new Error('Cannot demolish command center');
}
// Remove room
base.rooms.splice(roomIndex, 1);
this.updateBaseStats(base);
return {
success: true,
demolishedRoom: room
};
}
addDecoration(userId, decorationType) {
const base = this.getPlayerBase(userId);
const decorationTemplate = this.decorationTypes[decorationType];
if (!decorationTemplate) {
throw new Error('Invalid decoration type');
}
const decoration = {
type: decorationType,
placedAt: new Date().toISOString()
};
base.decorations.push(decoration);
return {
success: true,
decoration
};
}
removeDecoration(userId, decorationIndex) {
const base = this.getPlayerBase(userId);
if (decorationIndex < 0 || decorationIndex >= base.decorations.length) {
throw new Error('Invalid decoration index');
}
const removedDecoration = base.decorations.splice(decorationIndex, 1)[0];
return {
success: true,
removedDecoration
};
}
calculateTotalPowerCost(base) {
let totalCost = 0;
for (const room of base.rooms) {
const roomTemplate = this.roomTypes[room.type];
totalCost += roomTemplate.powerCost * room.level;
}
return totalCost;
}
updateBaseStats(base) {
// Reset to base values
base.power = 100;
base.maxPower = 100;
base.storage = 1000;
base.maxStorage = 1000;
base.defense = 10;
base.maxDefense = 10;
base.crewCapacity = 5;
base.maxCrewCapacity = 5;
base.researchBonus = 0;
base.craftingBonus = 0;
// Apply room bonuses
for (const room of base.rooms) {
const roomTemplate = this.roomTypes[room.type];
base.maxPower += roomTemplate.powerCost * room.level;
base.maxStorage += roomTemplate.storageBonus * room.level;
base.maxDefense += roomTemplate.powerCost * room.level * 0.5; // Defense rooms give defense bonus
if (room.type === 'barracks') {
base.maxCrewCapacity += 5 * room.level;
}
if (room.type === 'laboratory') {
base.researchBonus += 10 * room.level;
}
if (room.type === 'workshop') {
base.craftingBonus += 5 * room.level;
}
}
// Apply level bonuses
base.maxPower += 20 * (base.level - 1);
base.maxStorage += 200 * (base.level - 1);
base.maxDefense += 5 * (base.level - 1);
base.maxCrewCapacity += 2 * (base.level - 1);
}
getBaseStatistics(userId) {
const base = this.getPlayerBase(userId);
return {
name: base.name,
level: base.level,
experience: base.experience,
experienceToNext: base.experienceToNext,
rooms: base.rooms.length,
decorations: base.decorations.length,
power: base.power,
maxPower: base.maxPower,
storage: base.storage,
maxStorage: base.maxStorage,
defense: base.defense,
maxDefense: base.maxDefense,
crewCapacity: base.crewCapacity,
maxCrewCapacity: base.maxCrewCapacity,
researchBonus: base.researchBonus,
craftingBonus: base.craftingBonus,
totalPowerCost: this.calculateTotalPowerCost(base),
powerEfficiency: base.maxPower > 0 ? (base.power / base.maxPower) * 100 : 0,
storageEfficiency: base.maxStorage > 0 ? (base.storage / base.maxStorage) * 100 : 0
};
}
getAvailableRooms(userId) {
const base = this.getPlayerBase(userId);
const availableRooms = [];
for (const [roomType, template] of Object.entries(this.roomTypes)) {
// Skip if already built
if (base.rooms.find(room => room.type === roomType)) {
continue;
}
// Check level requirement
if (base.level >= template.requiredLevel) {
availableRooms.push({
type: roomType,
...template,
canAfford: true // In a real implementation, check player credits
});
}
}
return availableRooms;
}
getRoomUpgrades(userId) {
const base = this.getPlayerBase(userId);
const upgradableRooms = [];
for (const room of base.rooms) {
const template = this.roomTypes[room.type];
if (room.level < template.maxLevel) {
upgradableRooms.push({
type: room.type,
currentLevel: room.level,
maxLevel: template.maxLevel,
nextLevelCost: template.buildCost * room.level,
template
});
}
}
return upgradableRooms;
}
}
module.exports = BaseSystem;

View File

@ -0,0 +1,277 @@
/**
* Galaxy Strike Online - Server Crafting System
* Manages crafting recipes, materials, and item creation
*/
class CraftingSystem {
constructor() {
this.recipes = new Map();
this.playerCrafting = new Map(); // userId -> crafting data
this.initializeRecipes();
}
initializeRecipes() {
// Basic weapon recipes
this.addRecipe('basic_blaster', {
name: 'Basic Blaster',
type: 'weapon',
rarity: 'common',
description: 'A simple blaster for beginners',
materials: {
'iron_ore': 5,
'copper_wire': 3,
'energy_crystal': 1
},
results: {
'basic_blaster': 1,
'experience': 25
},
skillRequired: 1,
craftingTime: 5000 // 5 seconds
});
// Advanced weapon recipes
this.addRecipe('plasma_cannon', {
name: 'Plasma Cannon',
type: 'weapon',
rarity: 'rare',
description: 'A powerful plasma-based weapon',
materials: {
'iron_ore': 15,
'copper_wire': 10,
'energy_crystal': 5,
'rare_metal': 3
},
results: {
'plasma_cannon': 1,
'experience': 100
},
skillRequired: 10,
craftingTime: 15000 // 15 seconds
});
// Armor recipes
this.addRecipe('basic_armor', {
name: 'Basic Armor',
type: 'armor',
rarity: 'common',
description: 'Light protection for beginners',
materials: {
'iron_ore': 8,
'copper_wire': 2
},
results: {
'basic_armor': 1,
'experience': 30
},
skillRequired: 2,
craftingTime: 8000 // 8 seconds
});
// Consumable recipes
this.addRecipe('health_kit', {
name: 'Health Kit',
type: 'consumable',
rarity: 'common',
description: 'Restores health when used',
materials: {
'iron_ore': 2,
'energy_crystal': 1
},
results: {
'health_kit': 3,
'experience': 10
},
skillRequired: 1,
craftingTime: 2000 // 2 seconds
});
}
addRecipe(id, recipe) {
this.recipes.set(id, {
id,
...recipe,
createdAt: new Date().toISOString()
});
}
getRecipe(id) {
return this.recipes.get(id);
}
getAllRecipes() {
return Array.from(this.recipes.values());
}
getRecipesByType(type) {
return Array.from(this.recipes.values()).filter(recipe => recipe.type === type);
}
getRecipesByRarity(rarity) {
return Array.from(this.recipes.values()).filter(recipe => recipe.rarity === rarity);
}
initializePlayerData(userId) {
if (!this.playerCrafting.has(userId)) {
this.playerCrafting.set(userId, {
skill: 1,
experience: 0,
knownRecipes: new Set(['basic_blaster', 'health_kit']), // Start with basic recipes
craftingHistory: [],
totalCrafted: 0
});
}
return this.playerCrafting.get(userId);
}
getPlayerData(userId) {
return this.playerCrafting.get(userId) || this.initializePlayerData(userId);
}
canCraft(userId, recipeId, playerInventory) {
const recipe = this.getRecipe(recipeId);
if (!recipe) {
return { canCraft: false, reason: 'Recipe not found' };
}
const playerData = this.getPlayerData(userId);
// Check skill requirement
if (playerData.skill < recipe.skillRequired) {
return { canCraft: false, reason: 'Insufficient crafting skill' };
}
// Check materials
for (const [material, amount] of Object.entries(recipe.materials)) {
const playerAmount = playerInventory[material] || 0;
if (playerAmount < amount) {
return { canCraft: false, reason: `Insufficient ${material}` };
}
}
return { canCraft: true };
}
async craftItem(userId, recipeId, playerInventory) {
const canCraftResult = this.canCraft(userId, recipeId, playerInventory);
if (!canCraftResult.canCraft) {
throw new Error(canCraftResult.reason);
}
const recipe = this.getRecipe(recipeId);
const playerData = this.getPlayerData(userId);
// Remove materials from inventory
for (const [material, amount] of Object.entries(recipe.materials)) {
playerInventory[material] = (playerInventory[material] || 0) - amount;
}
// Add results to inventory
for (const [item, amount] of Object.entries(recipe.results)) {
if (item !== 'experience') {
playerInventory[item] = (playerInventory[item] || 0) + amount;
}
}
// Add experience and update stats
const experienceGained = recipe.results.experience || 0;
playerData.experience += experienceGained;
playerData.totalCrafted += 1;
// Check for skill up
const oldSkill = playerData.skill;
const newSkill = this.calculateSkillLevel(playerData.experience);
playerData.skill = newSkill;
// Record crafting history
playerData.craftingHistory.push({
recipeId,
timestamp: new Date().toISOString(),
experienceGained,
skillUp: newSkill > oldSkill
});
// Keep only last 100 crafting records
if (playerData.craftingHistory.length > 100) {
playerData.craftingHistory = playerData.craftingHistory.slice(-100);
}
return {
success: true,
recipe,
results: recipe.results,
experienceGained,
newSkill,
skillUp: newSkill > oldSkill,
playerInventory
};
}
calculateSkillLevel(experience) {
// Simple skill progression: 100 XP per level
return Math.floor(experience / 100) + 1;
}
learnRecipe(userId, recipeId) {
const recipe = this.getRecipe(recipeId);
if (!recipe) {
throw new Error('Recipe not found');
}
const playerData = this.getPlayerData(userId);
playerData.knownRecipes.add(recipeId);
return {
success: true,
recipeId,
recipeName: recipe.name
};
}
getPlayerKnownRecipes(userId) {
const playerData = this.getPlayerData(userId);
return Array.from(playerData.knownRecipes).map(recipeId => this.getRecipe(recipeId));
}
getCraftingStats(userId) {
const playerData = this.getPlayerData(userId);
return {
skill: playerData.skill,
experience: playerData.experience,
totalCrafted: playerData.totalCrafted,
knownRecipesCount: playerData.knownRecipes.size,
recentCrafting: playerData.craftingHistory.slice(-10)
};
}
discoverRecipe(userId, recipeId) {
const recipe = this.getRecipe(recipeId);
if (!recipe) {
throw new Error('Recipe not found');
}
const playerData = this.getPlayerData(userId);
if (playerData.knownRecipes.has(recipeId)) {
return { success: false, reason: 'Recipe already known' };
}
// Chance to discover based on skill level
const discoveryChance = Math.min(0.1 + (playerData.skill * 0.05), 0.5);
const discovered = Math.random() < discoveryChance;
if (discovered) {
playerData.knownRecipes.add(recipeId);
return {
success: true,
recipeId,
recipeName: recipe.name,
discovered: true
};
}
return { success: false, discovered: false };
}
}
module.exports = CraftingSystem;

View File

@ -0,0 +1,507 @@
/**
* Galaxy Strike Online - Server Dungeon System
* Manages dungeon instances, encounters, and rewards
*/
class DungeonSystem {
constructor() {
this.dungeons = new Map();
this.instances = new Map(); // instanceId -> dungeon instance
this.playerInstances = new Map(); // userId -> instanceId
this.initializeDungeons();
}
initializeDungeons() {
// Tutorial Dungeon
this.addDungeon('tutorial_caverns', {
name: 'Tutorial Caverns',
description: 'Learn the basics of dungeon crawling',
difficulty: 'easy',
minLevel: 1,
maxLevel: 5,
maxPlayers: 4,
estimatedTime: 15, // minutes
encounters: [
{
type: 'combat',
name: 'Cave Bats',
enemies: ['bat', 'bat', 'bat'],
difficulty: 1
},
{
type: 'combat',
name: 'Cave Spider',
enemies: ['spider'],
difficulty: 2
},
{
type: 'boss',
name: 'Cave Guardian',
enemies: ['guardian'],
difficulty: 3
}
],
rewards: {
experience: { min: 50, max: 100 },
credits: { min: 100, max: 250 },
items: ['health_kit', 'basic_weapon']
},
requirements: {
level: 1
}
});
// Combat Dungeon
this.addDungeon('abandoned_mines', {
name: 'Abandoned Mines',
description: 'Dangerous mines filled with hostile creatures',
difficulty: 'medium',
minLevel: 5,
maxLevel: 15,
maxPlayers: 4,
estimatedTime: 30,
encounters: [
{
type: 'combat',
name: 'Mine Guards',
enemies: ['guard', 'guard'],
difficulty: 3
},
{
type: 'trap',
name: 'Explosive Trap',
difficulty: 4
},
{
type: 'combat',
name: 'Mine Foreman',
enemies: ['foreman'],
difficulty: 5
},
{
type: 'treasure',
name: 'Hidden Cache',
difficulty: 3
},
{
type: 'boss',
name: 'Mine Overlord',
enemies: ['overlord'],
difficulty: 6
}
],
rewards: {
experience: { min: 200, max: 400 },
credits: { min: 500, max: 1000 },
items: ['rare_weapon', 'armor_piece']
},
requirements: {
level: 5,
item: 'torch'
}
});
// Puzzle Dungeon
this.addDungeon('ancient_library', {
name: 'Ancient Library',
description: 'Solve puzzles to uncover ancient knowledge',
difficulty: 'medium',
minLevel: 8,
maxLevel: 20,
maxPlayers: 2,
estimatedTime: 45,
encounters: [
{
type: 'puzzle',
name: 'Riddle Door',
difficulty: 4
},
{
type: 'combat',
name: 'Library Guardians',
enemies: ['guardian', 'guardian'],
difficulty: 4
},
{
type: 'puzzle',
name: 'Magic Lock',
difficulty: 5
},
{
type: 'treasure',
name: 'Forbidden Tome',
difficulty: 5
},
{
type: 'boss',
name: 'Librarian Specter',
enemies: ['specter'],
difficulty: 6
}
],
rewards: {
experience: { min: 300, max: 600 },
credits: { min: 750, max: 1500 },
items: ['spell_book', 'knowledge_scroll']
},
requirements: {
level: 8,
skill: 'ancient_knowledge'
}
});
// Raid Dungeon
this.addDungeon('dragon_lair', {
name: 'Dragon Lair',
description: 'Face the ultimate dragon challenge',
difficulty: 'hard',
minLevel: 15,
maxLevel: 30,
maxPlayers: 8,
estimatedTime: 60,
encounters: [
{
type: 'combat',
name: 'Dragon Whelps',
enemies: ['whelp', 'whelp', 'whelp', 'whelp'],
difficulty: 7
},
{
type: 'combat',
name: 'Dragon Guard',
enemies: ['dragon_guard', 'dragon_guard'],
difficulty: 8
},
{
type: 'trap',
name: 'Dragon Breath Trap',
difficulty: 9
},
{
type: 'combat',
name: 'Dragon Elite',
enemies: ['dragon_elite'],
difficulty: 10
},
{
type: 'boss',
name: 'Ancient Dragon',
enemies: ['ancient_dragon'],
difficulty: 12
}
],
rewards: {
experience: { min: 1000, max: 2000 },
credits: { min: 2500, max: 5000 },
items: ['dragon_scale', 'legendary_weapon', 'dragon_egg']
},
requirements: {
level: 15,
guild: true
}
});
}
addDungeon(id, dungeon) {
this.dungeons.set(id, {
id,
...dungeon,
createdAt: new Date().toISOString()
});
}
getDungeon(id) {
return this.dungeons.get(id);
}
getAllDungeons() {
return Array.from(this.dungeons.values());
}
getDungeonsByDifficulty(difficulty) {
return Array.from(this.dungeons.values()).filter(dungeon => dungeon.difficulty === difficulty);
}
createInstance(dungeonId, creatorId, playerIds = []) {
const dungeon = this.getDungeon(dungeonId);
if (!dungeon) {
throw new Error('Dungeon not found');
}
// Validate requirements
if (playerIds.length > dungeon.maxPlayers) {
throw new Error('Too many players for this dungeon');
}
const instanceId = `instance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const instance = {
id: instanceId,
dungeonId,
creatorId,
players: new Set([creatorId, ...playerIds]),
currentEncounter: 0,
status: 'active',
startedAt: new Date().toISOString(),
completedAt: null,
progress: {},
rewards: null,
deaths: new Map() // userId -> death count
};
// Initialize progress tracking
dungeon.encounters.forEach((encounter, index) => {
instance.progress[index] = {
completed: false,
attempts: 0,
deaths: 0
};
});
this.instances.set(instanceId, instance);
// Track player instances
for (const playerId of instance.players) {
this.playerInstances.set(playerId, instanceId);
}
return instance;
}
getInstance(instanceId) {
return this.instances.get(instanceId);
}
getPlayerInstance(userId) {
const instanceId = this.playerInstances.get(userId);
return instanceId ? this.getInstance(instanceId) : null;
}
joinInstance(instanceId, userId) {
const instance = this.getInstance(instanceId);
if (!instance) {
throw new Error('Instance not found');
}
if (instance.status !== 'active') {
throw new Error('Instance is not active');
}
const dungeon = this.getDungeon(instance.dungeonId);
if (instance.players.size >= dungeon.maxPlayers) {
throw new Error('Instance is full');
}
// Remove from existing instance if any
const existingInstanceId = this.playerInstances.get(userId);
if (existingInstanceId) {
this.leaveInstance(userId);
}
// Add to new instance
instance.players.add(userId);
this.playerInstances.set(userId, instanceId);
return instance;
}
leaveInstance(userId) {
const instanceId = this.playerInstances.get(userId);
if (!instanceId) {
return null;
}
const instance = this.getInstance(instanceId);
if (instance) {
instance.players.delete(userId);
// If instance is empty, clean it up
if (instance.players.size === 0) {
this.instances.delete(instanceId);
}
}
this.playerInstances.delete(userId);
return instance;
}
startEncounter(instanceId, userId) {
const instance = this.getInstance(instanceId);
if (!instance) {
throw new Error('Instance not found');
}
if (!instance.players.has(userId)) {
throw new Error('Player not in instance');
}
const dungeon = this.getDungeon(instance.dungeonId);
const encounter = dungeon.encounters[instance.currentEncounter];
if (!encounter) {
throw new Error('No more encounters');
}
return {
encounter,
encounterIndex: instance.currentEncounter,
instance
};
}
completeEncounter(instanceId, userId, result) {
const instance = this.getInstance(instanceId);
if (!instance) {
throw new Error('Instance not found');
}
const dungeon = this.getDungeon(instance.dungeonId);
const encounter = dungeon.encounters[instance.currentEncounter];
if (!encounter) {
throw new Error('No encounter to complete');
}
const progress = instance.progress[instance.currentEncounter];
progress.completed = true;
progress.attempts++;
if (result.deaths) {
progress.deaths += result.deaths;
instance.deaths.set(userId, (instance.deaths.get(userId) || 0) + result.deaths);
}
// Move to next encounter
instance.currentEncounter++;
// Check if dungeon is complete
if (instance.currentEncounter >= dungeon.encounters.length) {
return this.completeDungeon(instanceId);
}
return {
success: true,
nextEncounter: instance.currentEncounter < dungeon.encounters.length ? dungeon.encounters[instance.currentEncounter] : null,
instance
};
}
completeDungeon(instanceId) {
const instance = this.getInstance(instanceId);
if (!instance) {
throw new Error('Instance not found');
}
const dungeon = this.getDungeon(instance.dungeonId);
// Calculate rewards
const rewards = this.calculateRewards(dungeon, instance);
// Mark as completed
instance.status = 'completed';
instance.completedAt = new Date().toISOString();
instance.rewards = rewards;
// Remove players from instance tracking
for (const playerId of instance.players) {
this.playerInstances.delete(playerId);
}
return {
success: true,
dungeon,
rewards,
instance
};
}
calculateRewards(dungeon, instance) {
const rewards = {
experience: 0,
credits: 0,
items: []
};
// Calculate base rewards
const expRange = dungeon.rewards.experience;
const creditRange = dungeon.rewards.credits;
rewards.experience = Math.floor(Math.random() * (expRange.max - expRange.min + 1)) + expRange.min;
rewards.credits = Math.floor(Math.random() * (creditRange.max - creditRange.min + 1)) + creditRange.min;
// Add death penalty
const totalDeaths = Array.from(instance.deaths.values()).reduce((sum, deaths) => sum + deaths, 0);
const deathPenalty = Math.floor(totalDeaths * 0.1); // 10% penalty per death
rewards.experience = Math.max(0, rewards.experience - deathPenalty);
rewards.credits = Math.max(0, rewards.credits - (deathPenalty * 2));
// Add items (chance based on performance)
const itemChance = Math.max(0.1, 0.5 - (totalDeaths * 0.1)); // Lower chance with more deaths
if (Math.random() < itemChance) {
const itemPool = dungeon.rewards.items;
if (itemPool.length > 0) {
rewards.items.push(itemPool[Math.floor(Math.random() * itemPool.length)]);
}
}
return rewards;
}
getAvailableDungeons(playerLevel, playerCount = 1) {
return Array.from(this.dungeons.values()).filter(dungeon =>
dungeon.minLevel <= playerLevel &&
dungeon.maxLevel >= playerLevel &&
dungeon.maxPlayers >= playerCount
);
}
getDungeonStatistics(userId) {
const instances = Array.from(this.instances.values()).filter(instance =>
instance.players.has(userId)
);
const completed = instances.filter(instance => instance.status === 'completed');
const active = instances.filter(instance => instance.status === 'active');
return {
totalDungeons: instances.length,
completedDungeons: completed.length,
activeDungeons: active.length,
totalDeaths: Array.from(this.playerInstances.values()).length,
recentCompletions: completed.slice(-5).map(instance => ({
dungeonId: instance.dungeonId,
completedAt: instance.completedAt,
rewards: instance.rewards
}))
};
}
cleanupExpiredInstances() {
const now = Date.now();
const expiredInstances = [];
for (const [instanceId, instance] of this.instances) {
const age = now - new Date(instance.startedAt).getTime();
const maxAge = 2 * 60 * 60 * 1000; // 2 hours
if (age > maxAge && instance.status === 'active') {
expiredInstances.push(instanceId);
}
}
// Clean up expired instances
for (const instanceId of expiredInstances) {
const instance = this.instances.get(instanceId);
if (instance) {
for (const playerId of instance.players) {
this.playerInstances.delete(playerId);
}
this.instances.delete(instanceId);
}
}
return expiredInstances.length;
}
}
module.exports = DungeonSystem;

View File

@ -0,0 +1,272 @@
/**
* Galaxy Strike Online - Server Idle System
* Manages offline progression and idle mechanics
*/
class IdleSystem {
constructor() {
// Idle settings
this.maxOfflineTime = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
this.playerLastActive = new Map(); // userId -> last active timestamp
this.playerProductionRates = new Map(); // userId -> production rates
// Default production rates
this.defaultProductionRates = {
credits: 10, // credits per second (increased for better gameplay)
experience: 1, // experience per second (increased for better progression)
energy: 0.5 // energy regeneration per second
};
// Idle rewards
this.offlineRewards = {
credits: 0,
experience: 0,
energy: 0,
items: []
};
// Idle bonuses
this.bonuses = {
premium: 1.0,
guild: 1.0,
research: 1.0
};
// Idle achievements
this.achievements = {
totalOfflineTime: 0,
maxOfflineSession: 0,
totalIdleCredits: 0,
totalIdleExperience: 0
};
}
initializePlayerData(userId) {
if (!this.playerLastActive.has(userId)) {
this.playerLastActive.set(userId, Date.now());
this.playerProductionRates.set(userId, { ...this.defaultProductionRates });
this.playerAchievements.set(userId, {
totalOfflineTime: 0,
maxOfflineSession: 0,
totalIdleCredits: 0,
totalIdleExperience: 0
});
}
}
updateLastActive(userId) {
this.playerLastActive.set(userId, Date.now());
}
updateProductionRates(userId, rates) {
this.playerProductionRates.set(userId, {
...this.defaultProductionRates,
...rates
});
}
calculateOfflineProgress(userId) {
this.initializePlayerData(userId);
const lastActive = this.playerLastActive.get(userId);
const currentTime = Date.now();
const offlineTime = currentTime - lastActive;
// Cap offline time to maximum
const cappedOfflineTime = Math.min(offlineTime, this.maxOfflineTime);
if (cappedOfflineTime <= 0) {
return {
offlineTime: 0,
rewards: {
credits: 0,
experience: 0,
energy: 0,
items: []
}
};
}
const productionRates = this.playerProductionRates.get(userId);
const achievements = this.playerAchievements.get(userId);
// Calculate offline rewards
const offlineSeconds = Math.floor(cappedOfflineTime / 1000);
const rewards = {
credits: Math.floor(productionRates.credits * offlineSeconds),
experience: Math.floor(productionRates.experience * offlineSeconds),
energy: Math.floor(productionRates.energy * offlineSeconds),
items: []
};
// Update achievements
achievements.totalOfflineTime += cappedOfflineTime;
achievements.maxOfflineSession = Math.max(achievements.maxOfflineSession, cappedOfflineTime);
achievements.totalIdleCredits += rewards.credits;
achievements.totalIdleExperience += rewards.experience;
// Update last active time
this.playerLastActive.set(userId, currentTime);
return {
offlineTime: cappedOfflineTime,
offlineSeconds,
rewards,
productionRates
};
}
applyIdleRewards(userId, playerData) {
const progress = this.calculateOfflineProgress(userId);
if (progress.offlineTime > 0) {
// Apply rewards to player data
if (playerData.stats) {
playerData.stats.credits = (playerData.stats.credits || 0) + progress.rewards.credits;
playerData.stats.experience = (playerData.stats.experience || 0) + progress.rewards.experience;
}
if (playerData.resources) {
playerData.resources.energy = (playerData.resources.energy || 0) + progress.rewards.energy;
}
// Add items if any
if (progress.rewards.items && progress.rewards.items.length > 0) {
if (!playerData.inventory) {
playerData.inventory = [];
}
playerData.inventory.push(...progress.rewards.items);
}
}
return progress;
}
getProductionRates(userId) {
this.initializePlayerData(userId);
return this.playerProductionRates.get(userId);
}
updateBonuses(userId, bonuses) {
this.initializePlayerData(userId);
const currentRates = this.playerProductionRates.get(userId);
// Apply bonuses to production rates
const bonusMultiplier =
(bonuses.premium || this.defaultBonuses.premium) *
(bonuses.guild || this.defaultBonuses.guild) *
(bonuses.research || this.defaultBonuses.research);
const updatedRates = {};
for (const [resource, baseRate] of Object.entries(this.defaultProductionRates)) {
updatedRates[resource] = Math.floor(baseRate * bonusMultiplier);
}
this.playerProductionRates.set(userId, updatedRates);
return updatedRates;
}
getAchievements(userId) {
this.initializePlayerData(userId);
return this.playerAchievements.get(userId);
}
getIdleStats(userId) {
this.initializePlayerData(userId);
const achievements = this.playerAchievements.get(userId);
const productionRates = this.playerProductionRates.get(userId);
return {
lastActive: this.playerLastActive.get(userId),
productionRates,
achievements,
maxOfflineTime: this.maxOfflineTime,
currentOfflineTime: Date.now() - this.playerLastActive.get(userId)
};
}
// Calculate idle income for a specific time period
calculateIncomeForTime(userId, timeInSeconds) {
this.initializePlayerData(userId);
const productionRates = this.playerProductionRates.get(userId);
return {
credits: Math.floor(productionRates.credits * timeInSeconds),
experience: Math.floor(productionRates.experience * timeInSeconds),
energy: Math.floor(productionRates.energy * timeInSeconds)
};
}
// Get time until next milestone
getTimeUntilMilestone(userId, targetCredits = 10000) {
this.initializePlayerData(userId);
const productionRates = this.playerProductionRates.get(userId);
const currentOfflineTime = Date.now() - this.playerLastActive.get(userId);
if (productionRates.credits <= 0) {
return { seconds: Infinity, hours: Infinity };
}
const creditsPerSecond = productionRates.credits;
const secondsNeeded = Math.ceil(targetCredits / creditsPerSecond);
return {
seconds: secondsNeeded,
minutes: Math.ceil(secondsNeeded / 60),
hours: Math.ceil(secondsNeeded / 3600)
};
}
// Process idle rewards for multiple users (batch processing)
processBatchIdleRewards(userIds) {
const results = new Map();
for (const userId of userIds) {
try {
const progress = this.calculateOfflineProgress(userId);
results.set(userId, {
success: true,
progress
});
} catch (error) {
results.set(userId, {
success: false,
error: error.message
});
}
}
return results;
}
// Reset idle data for a user
resetPlayerData(userId) {
this.playerLastActive.delete(userId);
this.playerProductionRates.delete(userId);
this.playerAchievements.delete(userId);
}
// Get global idle statistics
getGlobalStats() {
const totalPlayers = this.playerLastActive.size;
let totalOfflineTime = 0;
let totalIdleCredits = 0;
let totalIdleExperience = 0;
for (const achievements of this.playerAchievements.values()) {
totalOfflineTime += achievements.totalOfflineTime;
totalIdleCredits += achievements.totalIdleCredits;
totalIdleExperience += achievements.totalIdleExperience;
}
return {
totalPlayers,
totalOfflineTime,
totalIdleCredits,
totalIdleExperience,
averageOfflineTime: totalPlayers > 0 ? totalOfflineTime / totalPlayers : 0
};
}
}
module.exports = IdleSystem;

View File

@ -0,0 +1,439 @@
/**
* Galaxy Strike Online - Server Quest System
* Manages quests, progress tracking, and rewards
*/
class QuestSystem {
constructor() {
this.quests = new Map();
this.playerQuests = new Map(); // userId -> quest data
this.initializeQuests();
}
initializeQuests() {
// Tutorial quests
this.addQuest('tutorial_first_battle', {
name: 'First Battle',
description: 'Complete your first battle to learn the basics',
type: 'tutorial',
difficulty: 'easy',
requirements: {
battlesWon: 1
},
rewards: {
experience: 100,
credits: 500,
items: ['health_kit', 'energy_pack']
},
prerequisites: [],
repeatable: false,
timeLimit: null
});
// Combat quests
this.addQuest('warrior_path', {
name: 'Warrior Path',
description: 'Win 10 battles to prove your combat skills',
type: 'combat',
difficulty: 'medium',
requirements: {
battlesWon: 10,
enemiesDefeated: 50
},
rewards: {
experience: 500,
credits: 2000,
items: ['basic_armor']
},
prerequisites: ['tutorial_first_battle'],
repeatable: false,
timeLimit: null
});
// Crafting quests
this.addQuest('novice_crafter', {
name: 'Novice Crafter',
description: 'Craft 5 items to learn the basics of crafting',
type: 'crafting',
difficulty: 'easy',
requirements: {
itemsCrafted: 5
},
rewards: {
experience: 200,
credits: 1000,
craftingExperience: 100
},
prerequisites: [],
repeatable: false,
timeLimit: null
});
// Exploration quests
this.addQuest('explorer_spirit', {
name: 'Explorer Spirit',
description: 'Discover 5 different locations in the galaxy',
type: 'exploration',
difficulty: 'medium',
requirements: {
locationsDiscovered: 5
},
rewards: {
experience: 300,
credits: 1500,
items: ['navigation_device']
},
prerequisites: ['tutorial_first_battle'],
repeatable: false,
timeLimit: null
});
// Daily quests
this.addQuest('daily_bounty', {
name: 'Daily Bounty',
description: 'Complete 3 battles today',
type: 'daily',
difficulty: 'easy',
requirements: {
battlesWon: 3
},
rewards: {
experience: 150,
credits: 750
},
prerequisites: [],
repeatable: true,
timeLimit: 86400000 // 24 hours
});
// Weekly quests
this.addQuest('weekly_champion', {
name: 'Weekly Champion',
description: 'Win 20 battles this week',
type: 'weekly',
difficulty: 'hard',
requirements: {
battlesWon: 20
},
rewards: {
experience: 1000,
credits: 5000,
items: ['rare_weapon_part']
},
prerequisites: [],
repeatable: true,
timeLimit: 604800000 // 7 days
});
}
addQuest(id, quest) {
this.quests.set(id, {
id,
...quest,
createdAt: new Date().toISOString()
});
}
getQuest(id) {
return this.quests.get(id);
}
getAllQuests() {
return Array.from(this.quests.values());
}
getQuestsByType(type) {
return Array.from(this.quests.values()).filter(quest => quest.type === type);
}
getQuestsByDifficulty(difficulty) {
return Array.from(this.quests.values()).filter(quest => quest.difficulty === difficulty);
}
initializePlayerData(userId) {
if (!this.playerQuests.has(userId)) {
this.playerQuests.set(userId, {
activeQuests: new Map(),
completedQuests: new Map(),
questHistory: [],
totalQuestsCompleted: 0,
dailyQuestsCompleted: 0,
weeklyQuestsCompleted: 0
});
}
return this.playerQuests.get(userId);
}
getPlayerData(userId) {
return this.playerQuests.get(userId) || this.initializePlayerData(userId);
}
getAvailableQuests(userId) {
const playerData = this.getPlayerData(userId);
const availableQuests = [];
for (const [questId, quest] of this.quests) {
// Skip if already active or completed (for non-repeatable quests)
if (playerData.activeQuests.has(questId)) continue;
if (playerData.completedQuests.has(questId) && !quest.repeatable) continue;
// Check prerequisites
const prerequisitesMet = quest.prerequisites.every(prereqId =>
playerData.completedQuests.has(prereqId)
);
if (prerequisitesMet) {
availableQuests.push({
...quest,
status: playerData.activeQuests.has(questId) ? 'active' : 'available'
});
}
}
return availableQuests;
}
startQuest(userId, questId) {
const quest = this.getQuest(questId);
if (!quest) {
throw new Error('Quest not found');
}
const playerData = this.getPlayerData(userId);
// Check if quest is already active
if (playerData.activeQuests.has(questId)) {
throw new Error('Quest already active');
}
// Check if quest is completed and not repeatable
if (playerData.completedQuests.has(questId) && !quest.repeatable) {
throw new Error('Quest already completed');
}
// Check prerequisites
const prerequisitesMet = quest.prerequisites.every(prereqId =>
playerData.completedQuests.has(prereqId)
);
if (!prerequisitesMet) {
throw new Error('Prerequisites not met');
}
// Start the quest
const questProgress = {
questId,
startedAt: new Date().toISOString(),
progress: {},
completed: false,
expiredAt: quest.timeLimit ? new Date(Date.now() + quest.timeLimit).toISOString() : null
};
// Initialize progress tracking
for (const requirement of Object.keys(quest.requirements)) {
questProgress.progress[requirement] = 0;
}
playerData.activeQuests.set(questId, questProgress);
return {
success: true,
quest,
progress: questProgress
};
}
updateQuestProgress(userId, questId, progressUpdates) {
const playerData = this.getPlayerData(userId);
const activeQuest = playerData.activeQuests.get(questId);
if (!activeQuest) {
throw new Error('Quest not active');
}
// Check if quest is expired
if (activeQuest.expiredAt && new Date() > new Date(activeQuest.expiredAt)) {
playerData.activeQuests.delete(questId);
throw new Error('Quest expired');
}
const quest = this.getQuest(questId);
let updated = false;
// Update progress
for (const [requirement, amount] of Object.entries(progressUpdates)) {
if (quest.requirements[requirement] !== undefined) {
activeQuest.progress[requirement] = Math.min(
activeQuest.progress[requirement] + amount,
quest.requirements[requirement]
);
updated = true;
}
}
// Check if quest is completed
const isCompleted = Object.entries(quest.requirements).every(
([requirement, requiredAmount]) =>
activeQuest.progress[requirement] >= requiredAmount
);
if (isCompleted && !activeQuest.completed) {
activeQuest.completed = true;
activeQuest.completedAt = new Date().toISOString();
return {
success: true,
questCompleted: true,
quest,
progress: activeQuest.progress
};
}
return {
success: true,
questCompleted: false,
quest,
progress: activeQuest.progress,
updated
};
}
completeQuest(userId, questId) {
const playerData = this.getPlayerData(userId);
const activeQuest = playerData.activeQuests.get(questId);
if (!activeQuest) {
throw new Error('Quest not active');
}
if (!activeQuest.completed) {
throw new Error('Quest requirements not met');
}
const quest = this.getQuest(questId);
// Move to completed quests
playerData.completedQuests.set(questId, {
...activeQuest,
quest,
completedAt: new Date().toISOString()
});
// Remove from active quests
playerData.activeQuests.delete(questId);
// Update statistics
playerData.totalQuestsCompleted += 1;
if (quest.type === 'daily') {
playerData.dailyQuestsCompleted += 1;
} else if (quest.type === 'weekly') {
playerData.weeklyQuestsCompleted += 1;
}
// Add to history
playerData.questHistory.push({
questId,
questName: quest.name,
completedAt: new Date().toISOString(),
rewards: quest.rewards
});
// Keep only last 100 quest records
if (playerData.questHistory.length > 100) {
playerData.questHistory = playerData.questHistory.slice(-100);
}
return {
success: true,
quest,
rewards: quest.rewards,
statistics: {
totalCompleted: playerData.totalQuestsCompleted,
dailyCompleted: playerData.dailyQuestsCompleted,
weeklyCompleted: playerData.weeklyQuestsCompleted
}
};
}
getPlayerActiveQuests(userId) {
const playerData = this.getPlayerData(userId);
const activeQuests = [];
for (const [questId, progress] of playerData.activeQuests) {
const quest = this.getQuest(questId);
activeQuests.push({
...quest,
progress: progress.progress,
startedAt: progress.startedAt,
expiredAt: progress.expiredAt
});
}
return activeQuests;
}
getPlayerCompletedQuests(userId) {
const playerData = this.getPlayerData(userId);
const completedQuests = [];
for (const [questId, completion] of playerData.completedQuests) {
completedQuests.push({
...completion.quest,
completedAt: completion.completedAt,
progress: completion.progress
});
}
return completedQuests;
}
getQuestStatistics(userId) {
const playerData = this.getPlayerData(userId);
const availableQuests = this.getAvailableQuests(userId);
return {
activeQuests: playerData.activeQuests.size,
completedQuests: playerData.completedQuests.size,
availableQuests: availableQuests.length,
totalCompleted: playerData.totalQuestsCompleted,
dailyCompleted: playerData.dailyQuestsCompleted,
weeklyCompleted: playerData.weeklyQuestsCompleted,
recentHistory: playerData.questHistory.slice(-10)
};
}
resetDailyQuests(userId) {
const playerData = this.getPlayerData(userId);
// Remove completed daily quests
for (const [questId, quest] of this.quests) {
if (quest.type === 'daily') {
playerData.completedQuests.delete(questId);
playerData.activeQuests.delete(questId);
}
}
playerData.dailyQuestsCompleted = 0;
return { success: true, message: 'Daily quests reset' };
}
resetWeeklyQuests(userId) {
const playerData = this.getPlayerData(userId);
// Remove completed weekly quests
for (const [questId, quest] of this.quests) {
if (quest.type === 'weekly') {
playerData.completedQuests.delete(questId);
playerData.activeQuests.delete(questId);
}
}
playerData.weeklyQuestsCompleted = 0;
return { success: true, message: 'Weekly quests reset' };
}
}
module.exports = QuestSystem;

View File

@ -0,0 +1,488 @@
/**
* Galaxy Strike Online - Server Ship System
* Manages ship data, upgrades, and combat mechanics
*/
class ShipSystem {
constructor() {
this.shipTemplates = new Map();
this.playerShips = new Map(); // userId -> array of ships
this.initializeShipTemplates();
}
initializeShipTemplates() {
// Starter Ships
this.addShipTemplate('starter_cruiser', {
name: 'Starter Cruiser',
class: 'Cruiser',
description: 'A reliable cruiser for beginners',
rarity: 'Common',
baseStats: {
health: 100,
maxHealth: 100,
attack: 10,
defense: 5,
speed: 10,
energy: 50,
maxEnergy: 50
},
slots: {
weapon: 2,
armor: 1,
module: 1
},
requirements: {
level: 1,
credits: 0
},
experience: 0,
requiredExp: 100,
texture: 'assets/textures/ships/starter_cruiser.png'
});
this.addShipTemplate('light_fighter', {
name: 'Light Fighter',
class: 'Fighter',
description: 'Fast and agile fighter ship',
rarity: 'Common',
baseStats: {
health: 80,
maxHealth: 80,
attack: 15,
defense: 3,
speed: 15,
energy: 40,
maxEnergy: 40
},
slots: {
weapon: 3,
armor: 0,
module: 2
},
requirements: {
level: 3,
credits: 1000
},
experience: 0,
requiredExp: 150,
texture: 'assets/textures/ships/light_fighter.png'
});
this.addShipTemplate('heavy_bomber', {
name: 'Heavy Bomber',
class: 'Bomber',
description: 'Slow but powerful bomber',
rarity: 'Uncommon',
baseStats: {
health: 150,
maxHealth: 150,
attack: 25,
defense: 10,
speed: 5,
energy: 60,
maxEnergy: 60
},
slots: {
weapon: 4,
armor: 2,
module: 1
},
requirements: {
level: 5,
credits: 5000
},
experience: 0,
requiredExp: 200,
texture: 'assets/textures/ships/heavy_bomber.png'
});
this.addShipTemplate('exploration_vessel', {
name: 'Exploration Vessel',
class: 'Explorer',
description: 'Versatile ship for exploration missions',
rarity: 'Rare',
baseStats: {
health: 120,
maxHealth: 120,
attack: 12,
defense: 8,
speed: 12,
energy: 80,
maxEnergy: 80
},
slots: {
weapon: 2,
armor: 1,
module: 3
},
requirements: {
level: 8,
credits: 15000
},
experience: 0,
requiredExp: 300,
texture: 'assets/textures/ships/exploration_vessel.png'
});
this.addShipTemplate('battle_cruiser', {
name: 'Battle Cruiser',
class: 'Cruiser',
description: 'Heavy combat cruiser',
rarity: 'Rare',
baseStats: {
health: 200,
maxHealth: 200,
attack: 30,
defense: 15,
speed: 8,
energy: 70,
maxEnergy: 70
},
slots: {
weapon: 5,
armor: 3,
module: 2
},
requirements: {
level: 12,
credits: 50000
},
experience: 0,
requiredExp: 500,
texture: 'assets/textures/ships/battle_cruiser.png'
});
this.addShipTemplate('flagship', {
name: 'Flagship',
class: 'Capital',
description: 'Ultimate command vessel',
rarity: 'Legendary',
baseStats: {
health: 500,
maxHealth: 500,
attack: 50,
defense: 30,
speed: 6,
energy: 150,
maxEnergy: 150
},
slots: {
weapon: 8,
armor: 5,
module: 4
},
requirements: {
level: 20,
credits: 500000
},
experience: 0,
requiredExp: 1000,
texture: 'assets/textures/ships/flagship.png'
});
}
addShipTemplate(id, template) {
this.shipTemplates.set(id, {
id,
...template,
createdAt: new Date().toISOString()
});
}
getShipTemplate(id) {
return this.shipTemplates.get(id);
}
getShipTemplatesByClass(shipClass) {
return Array.from(this.shipTemplates.values()).filter(template => template.class === shipClass);
}
getShipTemplatesByRarity(rarity) {
return Array.from(this.shipTemplates.values()).filter(template => template.rarity === rarity);
}
getAllShipTemplates() {
return Array.from(this.shipTemplates.values());
}
initializePlayerShips(userId) {
if (!this.playerShips.has(userId)) {
this.playerShips.set(userId, []);
// Give starter ship to new players
const starterShip = this.createShip(userId, 'starter_cruiser');
if (starterShip) {
this.playerShips.get(userId).push(starterShip);
}
}
return this.playerShips.get(userId);
}
createShip(userId, templateId, customizations = {}) {
const template = this.shipTemplates.get(templateId);
if (!template) {
throw new Error(`Ship template not found: ${templateId}`);
}
const ship = {
id: `ship_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
userId,
templateId,
name: customizations.name || template.name,
class: template.class,
rarity: template.rarity,
level: customizations.level || 1,
experience: 0,
requiredExp: template.requiredExp,
stats: { ...template.baseStats },
maxStats: { ...template.baseStats },
slots: { ...template.slots },
equippedItems: {},
texture: template.texture,
status: 'active',
createdAt: new Date().toISOString(),
lastUsed: new Date().toISOString()
};
return ship;
}
getPlayerShips(userId) {
return this.playerShips.get(userId) || this.initializePlayerShips(userId);
}
getShip(userId, shipId) {
const ships = this.getPlayerShips(userId);
return ships.find(ship => ship.id === shipId);
}
getCurrentShip(userId) {
const ships = this.getPlayerShips(userId);
return ships.find(ship => ship.status === 'active' && ship.lastUsed === Math.max(...ships.map(s => new Date(s.lastUsed).getTime())));
}
setCurrentShip(userId, shipId) {
const ships = this.getPlayerShips(userId);
const ship = ships.find(s => s.id === shipId);
if (!ship) {
throw new Error('Ship not found');
}
// Update last used time for all ships
ships.forEach(s => {
if (s.id === shipId) {
s.status = 'active';
s.lastUsed = new Date().toISOString();
} else {
s.status = 'inactive';
}
});
return ship;
}
addExperience(userId, shipId, amount) {
const ship = this.getShip(userId, shipId);
if (!ship) {
throw new Error('Ship not found');
}
ship.experience += amount;
// Check for level up
let levelsGained = 0;
while (ship.experience >= ship.requiredExp) {
ship.experience -= ship.requiredExp;
ship.level += 1;
levelsGained++;
// Increase stats on level up
this.levelUpShip(ship);
// Update required experience for next level
ship.requiredExp = this.getExperienceNeeded(ship.level);
}
return {
experienceGained: amount,
levelsGained,
newLevel: ship.level,
newStats: ship.stats
};
}
levelUpShip(ship) {
const statIncrease = 1.1; // 10% increase per level
for (const [stat, value] of Object.entries(ship.stats)) {
if (typeof value === 'number') {
ship.stats[stat] = Math.floor(value * statIncrease);
ship.maxStats[stat] = Math.floor(ship.maxStats[stat] * statIncrease);
}
}
}
getExperienceNeeded(level) {
// Exponential experience curve
return Math.floor(100 * Math.pow(1.5, level - 1));
}
upgradeShip(userId, shipId, upgradeType) {
const ship = this.getShip(userId, shipId);
if (!ship) {
throw new Error('Ship not found');
}
switch (upgradeType) {
case 'health':
ship.stats.health = Math.floor(ship.stats.health * 1.2);
ship.stats.maxHealth = Math.floor(ship.stats.maxHealth * 1.2);
break;
case 'attack':
ship.stats.attack = Math.floor(ship.stats.attack * 1.15);
break;
case 'defense':
ship.stats.defense = Math.floor(ship.stats.defense * 1.15);
break;
case 'speed':
ship.stats.speed = Math.floor(ship.stats.speed * 1.1);
break;
case 'energy':
ship.stats.energy = Math.floor(ship.stats.energy * 1.2);
ship.stats.maxEnergy = Math.floor(ship.stats.maxEnergy * 1.2);
break;
default:
throw new Error('Invalid upgrade type');
}
return {
upgradeType,
newStats: ship.stats
};
}
equipItem(userId, shipId, slot, itemId) {
const ship = this.getShip(userId, shipId);
if (!ship) {
throw new Error('Ship not found');
}
if (!ship.slots[slot]) {
throw new Error('Invalid slot type');
}
ship.equippedItems[slot] = itemId;
return {
slot,
itemId,
equippedItems: ship.equippedItems
};
}
unequipItem(userId, shipId, slot) {
const ship = this.getShip(userId, shipId);
if (!ship) {
throw new Error('Ship not found');
}
const itemId = ship.equippedItems[slot];
delete ship.equippedItems[slot];
return {
slot,
itemId,
equippedItems: ship.equippedItems
};
}
repairShip(userId, shipId, amount = null) {
const ship = this.getShip(userId, shipId);
if (!ship) {
throw new Error('Ship not found');
}
if (amount === null) {
// Full repair
ship.stats.health = ship.stats.maxHealth;
} else {
// Partial repair
ship.stats.health = Math.min(ship.stats.health + amount, ship.stats.maxHealth);
}
return {
health: ship.stats.health,
maxHealth: ship.stats.maxHealth,
repaired: amount || (ship.stats.maxHealth - ship.stats.health)
};
}
deleteShip(userId, shipId) {
const ships = this.getPlayerShips(userId);
const shipIndex = ships.findIndex(ship => ship.id === shipId);
if (shipIndex === -1) {
throw new Error('Ship not found');
}
const deletedShip = ships.splice(shipIndex, 1)[0];
// If this was the current ship, set a new current ship
if (deletedShip.status === 'active' && ships.length > 0) {
ships[0].status = 'active';
ships[0].lastUsed = new Date().toISOString();
}
return deletedShip;
}
getShipClasses() {
const classes = new Set();
for (const template of this.shipTemplates.values()) {
classes.add(template.class);
}
return Array.from(classes).sort();
}
getShipRarities() {
const rarities = new Set();
for (const template of this.shipTemplates.values()) {
rarities.add(template.rarity);
}
return Array.from(rarities).sort();
}
getAvailableShips(userId, playerLevel = 1) {
return Array.from(this.shipTemplates.values())
.filter(template => template.requirements.level <= playerLevel)
.map(template => ({
id: template.id,
name: template.name,
class: template.class,
rarity: template.rarity,
description: template.description,
requirements: template.requirements,
baseStats: template.baseStats,
slots: template.slots,
texture: template.texture
}));
}
getPlayerShipStats(userId) {
const ships = this.getPlayerShips(userId);
return {
totalShips: ships.length,
activeShip: this.getCurrentShip(userId),
shipClasses: ships.reduce((acc, ship) => {
acc[ship.class] = (acc[ship.class] || 0) + 1;
return acc;
}, {}),
averageLevel: ships.length > 0 ? Math.floor(ships.reduce((sum, ship) => sum + ship.level, 0) / ships.length) : 0,
totalExperience: ships.reduce((sum, ship) => sum + ship.experience, 0)
};
}
}
module.exports = ShipSystem;

View File

@ -0,0 +1,397 @@
/**
* 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;