/** * 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;