489 lines
14 KiB
JavaScript
489 lines
14 KiB
JavaScript
/**
|
|
* 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;
|