moved the items call from the client to the server
@ -14,7 +14,7 @@ class GameInitializer {
|
|||||||
this.currentUser = null;
|
this.currentUser = null;
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server
|
this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server
|
||||||
this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server for Socket.IO (local dev server)
|
this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server for Socket.IO
|
||||||
|
|
||||||
console.log('[GAME INITIALIZER] Constructor - gameServerUrl set to:', this.gameServerUrl);
|
console.log('[GAME INITIALIZER] Constructor - gameServerUrl set to:', this.gameServerUrl);
|
||||||
}
|
}
|
||||||
@ -74,6 +74,7 @@ class GameInitializer {
|
|||||||
const FORCED_URL = 'https://dev.gameserver.galaxystrike.online';
|
const FORCED_URL = 'https://dev.gameserver.galaxystrike.online';
|
||||||
console.log('[GAME INITIALIZER] FORCING URL to:', FORCED_URL);
|
console.log('[GAME INITIALIZER] FORCING URL to:', FORCED_URL);
|
||||||
console.log('[GAME INITIALIZER] Original this.gameServerUrl:', this.gameServerUrl);
|
console.log('[GAME INITIALIZER] Original this.gameServerUrl:', this.gameServerUrl);
|
||||||
|
console.log('[GAME INITIALIZER] Using remote development server');
|
||||||
|
|
||||||
// Connect to the game server with FORCED URL
|
// Connect to the game server with FORCED URL
|
||||||
this.socket = io(FORCED_URL, {
|
this.socket = io(FORCED_URL, {
|
||||||
|
|||||||
@ -547,25 +547,36 @@ class Economy {
|
|||||||
const canAfford = this.canAfford(item.price, item.currency);
|
const canAfford = this.canAfford(item.price, item.currency);
|
||||||
const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
|
const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
|
||||||
|
|
||||||
|
// Generate image URL - server will serve images
|
||||||
|
const imageUrl = this.getItemImageUrl(item);
|
||||||
|
const placeholderUrl = 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="shop-item ${canAfford ? '' : 'cant-afford'} ${isOwned ? 'owned' : ''}" data-item-id="${item.id}">
|
<div class="shop-item ${canAfford ? '' : 'cant-afford'} ${isOwned ? 'owned' : ''}" data-item-id="${item.id}">
|
||||||
<div class="shop-item-content">
|
<div class="shop-item-content">
|
||||||
<div class="shop-item-header">
|
<div class="shop-item-image">
|
||||||
<h3 class="shop-item-name">${item.name}</h3>
|
<img src="${imageUrl}" alt="${item.name}"
|
||||||
<span class="shop-item-rarity ${item.rarity}">${item.rarity}</span>
|
onerror="this.src='${placeholderUrl}'"
|
||||||
|
loading="lazy">
|
||||||
</div>
|
</div>
|
||||||
<div class="shop-item-body">
|
<div class="shop-item-info">
|
||||||
<p class="shop-item-description">${item.description}</p>
|
<div class="shop-item-header">
|
||||||
<div class="shop-item-price">
|
<h3 class="shop-item-name">${item.name}</h3>
|
||||||
${this.formatPrice(item)}
|
<span class="shop-item-rarity ${item.rarity}">${item.rarity}</span>
|
||||||
|
</div>
|
||||||
|
<div class="shop-item-body">
|
||||||
|
<p class="shop-item-description">${item.description}</p>
|
||||||
|
<div class="shop-item-price">
|
||||||
|
${this.formatPrice(item)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="shop-item-footer">
|
||||||
|
<button class="shop-item-purchase-btn"
|
||||||
|
data-item-id="${item.id}"
|
||||||
|
${!canAfford || isOwned ? 'disabled' : ''}>
|
||||||
|
${isOwned ? 'Owned' : 'Purchase'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="shop-item-footer">
|
|
||||||
<button class="shop-item-purchase-btn"
|
|
||||||
data-item-id="${item.id}"
|
|
||||||
${!canAfford || isOwned ? 'disabled' : ''}>
|
|
||||||
${isOwned ? 'Owned' : 'Purchase'}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -582,6 +593,56 @@ class Economy {
|
|||||||
return `${price} ${currency}`;
|
return `${price} ${currency}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image URL for an item from server
|
||||||
|
*/
|
||||||
|
getItemImageUrl(item) {
|
||||||
|
if (!item) return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png';
|
||||||
|
|
||||||
|
// For multiplayer, ALWAYS get from server
|
||||||
|
if (window.smartSaveManager?.isMultiplayer && this.game.socket) {
|
||||||
|
const serverUrl = this.getServerUrl();
|
||||||
|
|
||||||
|
// Map item types to proper server paths
|
||||||
|
switch (item.type) {
|
||||||
|
case 'ship':
|
||||||
|
return `${serverUrl}/images/ships/${item.id}.png`;
|
||||||
|
case 'weapon':
|
||||||
|
return `${serverUrl}/images/weapons/${item.id}.png`;
|
||||||
|
case 'armor':
|
||||||
|
return `${serverUrl}/images/armors/${item.id}.png`;
|
||||||
|
case 'material':
|
||||||
|
return `${serverUrl}/images/items/materials/${item.id}.png`;
|
||||||
|
case 'consumable':
|
||||||
|
return `${serverUrl}/images/items/consumables/${item.id}.png`;
|
||||||
|
case 'cosmetic':
|
||||||
|
return `${serverUrl}/images/items/cosmetics/${item.id}.png`;
|
||||||
|
default:
|
||||||
|
return `${serverUrl}/images/ui/placeholder.png`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For singleplayer, use local texture path (if available)
|
||||||
|
if (item.texture) {
|
||||||
|
return item.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to server placeholder
|
||||||
|
return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get server URL for image requests
|
||||||
|
*/
|
||||||
|
getServerUrl() {
|
||||||
|
// Get server URL from socket connection
|
||||||
|
if (this.game.socket && this.game.socket.io && this.game.socket.io.uri) {
|
||||||
|
return this.game.socket.io.uri.replace('/socket.io', '');
|
||||||
|
}
|
||||||
|
// Fallback to environment variable or production server
|
||||||
|
return process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online';
|
||||||
|
}
|
||||||
|
|
||||||
// Save/Load functionality
|
// Save/Load functionality
|
||||||
save() {
|
save() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -123,9 +123,13 @@ class ItemSystem {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
window.game.socket.off('shopItemsReceived', handleResponse);
|
window.game.socket.off('shopItemsReceived', handleResponse);
|
||||||
|
|
||||||
|
console.log('[ITEM SYSTEM] Response success:', data.success);
|
||||||
|
console.log('[ITEM SYSTEM] Response items count:', data.items?.length || 0);
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
console.log('[ITEM SYSTEM] Successfully received', data.items?.length || 0, 'items');
|
console.log('[ITEM SYSTEM] Successfully received', data.items?.length || 0, 'items');
|
||||||
console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp);
|
console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp);
|
||||||
|
console.log('[ITEM SYSTEM] Sample item:', data.items?.[0]);
|
||||||
resolve(data.items || []);
|
resolve(data.items || []);
|
||||||
} else {
|
} else {
|
||||||
console.error('[ITEM SYSTEM] Server returned error:', data.error);
|
console.error('[ITEM SYSTEM] Server returned error:', data.error);
|
||||||
|
|||||||
@ -60,6 +60,22 @@ class ShipSystem {
|
|||||||
shipGrid.appendChild(shipCard);
|
shipGrid.appendChild(shipCard);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ship image URL from server or local
|
||||||
|
*/
|
||||||
|
getShipImageUrl(ship) {
|
||||||
|
if (!ship) return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png';
|
||||||
|
|
||||||
|
// For multiplayer, get from server
|
||||||
|
if (window.smartSaveManager?.isMultiplayer && window.game?.socket) {
|
||||||
|
const serverUrl = window.game.socket.io?.uri?.replace('/socket.io', '') || process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online';
|
||||||
|
return `${serverUrl}/images/ships/${ship.id}.png`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For singleplayer, use local path
|
||||||
|
return ship.image || ship.texture || 'assets/textures/ships/starter_cruiser.png';
|
||||||
|
}
|
||||||
|
|
||||||
createShipCard(ship) {
|
createShipCard(ship) {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
@ -68,17 +84,13 @@ class ShipSystem {
|
|||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="ship-card-header">
|
<div class="ship-card-header">
|
||||||
<img src="${ship.image}" alt="${ship.name}" class="ship-card-image">
|
<img src="${this.getShipImageUrl(ship)}" alt="${ship.name}"
|
||||||
|
onerror="this.src='${window.smartSaveManager?.isMultiplayer ? 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png' : 'assets/textures/missing-texture.png'}'"
|
||||||
|
class="ship-card-image">
|
||||||
<div class="ship-card-info">
|
<div class="ship-card-info">
|
||||||
<div class="ship-card-rarity ${ship.rarity.toLowerCase()}">${ship.rarity}</div>
|
<div class="ship-card-rarity ${ship.rarity.toLowerCase()}">${ship.rarity}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ship-card-actions">
|
|
||||||
<button class="btn-action btn-switch" onclick="game.systems.ship.switchShip('${ship.id}')"
|
|
||||||
${ship.status === 'active' ? 'disabled' : ''}>
|
|
||||||
${ship.status === 'active' ? 'ACTIVE' : 'SWITCH'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return card;
|
return card;
|
||||||
@ -106,10 +118,22 @@ class ShipSystem {
|
|||||||
const ship = player.ship;
|
const ship = player.ship;
|
||||||
|
|
||||||
if (elements.currentShipImage) {
|
if (elements.currentShipImage) {
|
||||||
// Use the ship's texture if available, otherwise fallback
|
// Use server image for multiplayer, local for singleplayer
|
||||||
const imagePath = ship.texture || `assets/textures/ships/starter_cruiser.png`;
|
let imagePath;
|
||||||
|
if (window.smartSaveManager?.isMultiplayer && window.game?.socket) {
|
||||||
|
const serverUrl = window.game.socket.io?.uri?.replace('/socket.io', '') || 'http://localhost:3002';
|
||||||
|
imagePath = `${serverUrl}/images/ships/${ship.class || 'starter_cruiser'}.png`;
|
||||||
|
} else {
|
||||||
|
imagePath = ship.texture || `assets/textures/ships/starter_cruiser.png`;
|
||||||
|
}
|
||||||
|
|
||||||
elements.currentShipImage.src = imagePath;
|
elements.currentShipImage.src = imagePath;
|
||||||
elements.currentShipImage.alt = ship.name;
|
elements.currentShipImage.alt = ship.name;
|
||||||
|
elements.currentShipImage.onerror = function() {
|
||||||
|
this.src = window.smartSaveManager?.isMultiplayer ?
|
||||||
|
'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png' :
|
||||||
|
'assets/textures/missing-texture.png';
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (elements.currentShipName) elements.currentShipName.textContent = ship.name;
|
if (elements.currentShipName) elements.currentShipName.textContent = ship.name;
|
||||||
if (elements.currentShipClass) elements.currentShipClass.textContent = ship.class || 'Unknown';
|
if (elements.currentShipClass) elements.currentShipClass.textContent = ship.class || 'Unknown';
|
||||||
|
|||||||
@ -24,7 +24,7 @@ class LiveMainMenu {
|
|||||||
this.currentUser = null;
|
this.currentUser = null;
|
||||||
this.servers = []; // Renamed from serverList to avoid conflict with DOM element
|
this.servers = []; // Renamed from serverList to avoid conflict with DOM element
|
||||||
this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server URL
|
this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server URL
|
||||||
this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server URL for Socket.IO (local dev server)
|
this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server URL for Socket.IO
|
||||||
this.localGameServerUrl = 'http://localhost:3002'; // Local Game Server URL
|
this.localGameServerUrl = 'http://localhost:3002'; // Local Game Server URL
|
||||||
this.isLocalMode = false; // Track if we're in local mode
|
this.isLocalMode = false; // Track if we're in local mode
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
0
GameServer/assets/images/items/cosmetics/temp
Normal file
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 312 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
0
GameServer/assets/images/ui/icons/temp
Normal file
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
191
GameServer/create_placeholders.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* Create placeholder images for existing items in ItemSystem
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Create directories if they don't exist
|
||||||
|
const directories = [
|
||||||
|
'assets/images/ships',
|
||||||
|
'assets/images/weapons',
|
||||||
|
'assets/images/armors',
|
||||||
|
'assets/images/items/materials',
|
||||||
|
'assets/images/items/consumables',
|
||||||
|
'assets/images/items/cosmetics',
|
||||||
|
'assets/images/ui'
|
||||||
|
];
|
||||||
|
|
||||||
|
directories.forEach(dir => {
|
||||||
|
const fullPath = path.join(__dirname, dir);
|
||||||
|
if (!fs.existsSync(fullPath)) {
|
||||||
|
fs.mkdirSync(fullPath, { recursive: true });
|
||||||
|
console.log(`Created directory: ${dir}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a simple colored rectangle as placeholder
|
||||||
|
function createPlaceholder(width, height, color, text) {
|
||||||
|
// Create a simple 1x1 pixel colored PNG (base64 encoded)
|
||||||
|
// This is a minimal PNG with transparency
|
||||||
|
const createColorPNG = (r, g, b) => {
|
||||||
|
return Buffer.from([
|
||||||
|
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
|
||||||
|
0x00, 0x00, 0x00, 0x0D, // IHDR chunk length
|
||||||
|
0x49, 0x48, 0x44, 0x52, // IHDR
|
||||||
|
width >> 24, width >> 16, width >> 8, width, // width
|
||||||
|
height >> 24, height >> 16, height >> 8, height, // height
|
||||||
|
0x08, 0x02, 0x00, 0x00, 0x00, // bit depth, color type, compression, filter, interlace
|
||||||
|
0x4B, 0x6D, 0x29, 0xDC, // CRC
|
||||||
|
0x00, 0x00, 0x00, 0x0C, // IDAT chunk length
|
||||||
|
0x49, 0x44, 0x41, 0x54, // IDAT
|
||||||
|
0x08, 0x99, 0x01, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, // compressed data
|
||||||
|
0x00, 0x00, 0x00, 0x00, // IEND chunk length
|
||||||
|
0x49, 0x45, 0x4E, 0x44, // IEND
|
||||||
|
0xAE, 0x42, 0x60, 0x82 // CRC
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// For simplicity, let's copy existing images or create basic placeholders
|
||||||
|
return createColorPNG(
|
||||||
|
color === 'blue' ? 52 : color === 'red' ? 231 : color === 'green' ? 39 : 149,
|
||||||
|
color === 'blue' ? 152 : color === 'red' ? 76 : color === 'green' ? 174 : 165,
|
||||||
|
color === 'blue' ? 219 : color === 'red' ? 60 : color === 'green' ? 96 : 166
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items from your ItemSystem
|
||||||
|
const items = {
|
||||||
|
ships: [
|
||||||
|
'starter_cruiser_common',
|
||||||
|
'starter_cruiser_uncommon',
|
||||||
|
'starter_cruiser_rare',
|
||||||
|
'interceptor_common',
|
||||||
|
'interceptor_uncommon'
|
||||||
|
],
|
||||||
|
weapons: [
|
||||||
|
'laser_pistol_common',
|
||||||
|
'laser_pistol_uncommon',
|
||||||
|
'laser_pistol_rare',
|
||||||
|
'plasma_rifle_common',
|
||||||
|
'plasma_rifle_uncommon',
|
||||||
|
'plasma_rifle_rare'
|
||||||
|
],
|
||||||
|
armors: [
|
||||||
|
'light_armor_common',
|
||||||
|
'light_armor_uncommon',
|
||||||
|
'light_armor_rare',
|
||||||
|
'medium_armor_common',
|
||||||
|
'medium_armor_uncommon',
|
||||||
|
'medium_armor_rare'
|
||||||
|
],
|
||||||
|
materials: [
|
||||||
|
'steel_plating',
|
||||||
|
'energy_crystal',
|
||||||
|
'rare_metal',
|
||||||
|
'quantum_core',
|
||||||
|
'nanomaterials',
|
||||||
|
'dark_matter_fragment'
|
||||||
|
],
|
||||||
|
consumables: [
|
||||||
|
'health_pack',
|
||||||
|
'energy_boost',
|
||||||
|
'shield_recharge',
|
||||||
|
'repair_kit',
|
||||||
|
'ammo_pack',
|
||||||
|
'experience_boost'
|
||||||
|
],
|
||||||
|
cosmetics: [
|
||||||
|
'cool_paint_job',
|
||||||
|
'neon_lights',
|
||||||
|
'custom_decal',
|
||||||
|
'golden_trim',
|
||||||
|
'carbon_fiber',
|
||||||
|
'chrome_finish'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy existing images or create placeholders
|
||||||
|
console.log('Creating placeholder images...');
|
||||||
|
|
||||||
|
// For ships - copy existing or create placeholder
|
||||||
|
items.ships.forEach(shipId => {
|
||||||
|
const existingPath = path.join(__dirname, `assets/images/ships/starter_cruiser.png`);
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/ships/${shipId}.png`);
|
||||||
|
|
||||||
|
if (fs.existsSync(existingPath) && !fs.existsSync(targetPath)) {
|
||||||
|
fs.copyFileSync(existingPath, targetPath);
|
||||||
|
console.log(`Copied ship: ${shipId}.png`);
|
||||||
|
} else if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(80, 80, 'blue', shipId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created ship placeholder: ${shipId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For weapons - copy existing or create placeholder
|
||||||
|
items.weapons.forEach(weaponId => {
|
||||||
|
const existingPath = path.join(__dirname, `assets/images/weapons/starter_blaster.png`);
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/weapons/${weaponId}.png`);
|
||||||
|
|
||||||
|
if (fs.existsSync(existingPath) && !fs.existsSync(targetPath)) {
|
||||||
|
fs.copyFileSync(existingPath, targetPath);
|
||||||
|
console.log(`Copied weapon: ${weaponId}.png`);
|
||||||
|
} else if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(64, 64, 'red', weaponId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created weapon placeholder: ${weaponId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For armors - create placeholders
|
||||||
|
items.armors.forEach(armorId => {
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/armors/${armorId}.png`);
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(64, 64, 'green', armorId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created armor placeholder: ${armorId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For materials - create placeholders
|
||||||
|
items.materials.forEach(materialId => {
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/items/materials/${materialId}.png`);
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(48, 48, 'gray', materialId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created material placeholder: ${materialId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For consumables - create placeholders
|
||||||
|
items.consumables.forEach(consumableId => {
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/items/consumables/${consumableId}.png`);
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(48, 48, 'purple', consumableId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created consumable placeholder: ${consumableId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For cosmetics - create placeholders
|
||||||
|
items.cosmetics.forEach(cosmeticId => {
|
||||||
|
const targetPath = path.join(__dirname, `assets/images/items/cosmetics/${cosmeticId}.png`);
|
||||||
|
if (!fs.existsSync(targetPath)) {
|
||||||
|
const placeholder = createPlaceholder(64, 64, 'gold', cosmeticId);
|
||||||
|
fs.writeFileSync(targetPath, placeholder);
|
||||||
|
console.log(`Created cosmetic placeholder: ${cosmeticId}.png`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create UI placeholder
|
||||||
|
const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
|
||||||
|
if (!fs.existsSync(placeholderPath)) {
|
||||||
|
const placeholder = createPlaceholder(80, 80, 'gray', 'placeholder');
|
||||||
|
fs.writeFileSync(placeholderPath, placeholder);
|
||||||
|
console.log('Created UI placeholder.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n🎉 Placeholder images created successfully!');
|
||||||
|
console.log('📦 Your shop should now display items with placeholder images!');
|
||||||
|
console.log('🔄 Replace these with actual images as you create them.');
|
||||||
209
GameServer/generate_assets.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/**
|
||||||
|
* Generate temporary placeholder images for all item types
|
||||||
|
* Run this script with: node generate_assets.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Create directories
|
||||||
|
const directories = [
|
||||||
|
'assets/images/ships',
|
||||||
|
'assets/images/weapons',
|
||||||
|
'assets/images/armors',
|
||||||
|
'assets/images/items/materials',
|
||||||
|
'assets/images/items/consumables',
|
||||||
|
'assets/images/items/cosmetics',
|
||||||
|
'assets/images/ui/icons'
|
||||||
|
];
|
||||||
|
|
||||||
|
directories.forEach(dir => {
|
||||||
|
const fullPath = path.join(__dirname, dir);
|
||||||
|
if (!fs.existsSync(fullPath)) {
|
||||||
|
fs.mkdirSync(fullPath, { recursive: true });
|
||||||
|
console.log(`Created directory: ${dir}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate simple SVG placeholder
|
||||||
|
function generateSVG(width, height, color, text, filename) {
|
||||||
|
const svg = `
|
||||||
|
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="${width}" height="${height}" fill="${color}"/>
|
||||||
|
<text x="50%" y="50%" text-anchor="middle" dy=".3em"
|
||||||
|
font-family="Arial, sans-serif" font-size="12" fill="white">
|
||||||
|
${text}
|
||||||
|
</text>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ships - Blue theme
|
||||||
|
const ships = [
|
||||||
|
'starter_cruiser_common',
|
||||||
|
'starter_cruiser_uncommon',
|
||||||
|
'starter_cruiser_rare',
|
||||||
|
'advanced_fighter_common',
|
||||||
|
'advanced_fighter_uncommon',
|
||||||
|
'advanced_fighter_rare',
|
||||||
|
'heavy_battleship_common',
|
||||||
|
'heavy_battleship_uncommon',
|
||||||
|
'heavy_battleship_rare',
|
||||||
|
'stealth_ship_common',
|
||||||
|
'stealth_ship_uncommon',
|
||||||
|
'stealth_ship_rare'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Weapons - Red theme
|
||||||
|
const weapons = [
|
||||||
|
'laser_cannon_common',
|
||||||
|
'laser_cannon_uncommon',
|
||||||
|
'laser_cannon_rare',
|
||||||
|
'plasma_rifle_common',
|
||||||
|
'plasma_rifle_uncommon',
|
||||||
|
'plasma_rifle_rare',
|
||||||
|
'missile_launcher_common',
|
||||||
|
'missile_launcher_uncommon',
|
||||||
|
'missile_launcher_rare',
|
||||||
|
'railgun_common',
|
||||||
|
'railgun_uncommon',
|
||||||
|
'railgun_rare'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Armors - Green theme
|
||||||
|
const armors = [
|
||||||
|
'light_armor_common',
|
||||||
|
'light_armor_uncommon',
|
||||||
|
'light_armor_rare',
|
||||||
|
'medium_armor_common',
|
||||||
|
'medium_armor_uncommon',
|
||||||
|
'medium_armor_rare',
|
||||||
|
'heavy_armor_common',
|
||||||
|
'heavy_armor_uncommon',
|
||||||
|
'heavy_armor_rare',
|
||||||
|
'energy_shield_common',
|
||||||
|
'energy_shield_uncommon',
|
||||||
|
'energy_shield_rare'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Materials - Gray theme
|
||||||
|
const materials = [
|
||||||
|
'metal_scraps',
|
||||||
|
'energy_crystal',
|
||||||
|
'plastic_parts',
|
||||||
|
'electronic_components',
|
||||||
|
'rare_earth_metal',
|
||||||
|
'quantum_core',
|
||||||
|
'nanomaterials',
|
||||||
|
'dark_matter_fragment',
|
||||||
|
'solar_panel',
|
||||||
|
'cooling_system',
|
||||||
|
'power_cell',
|
||||||
|
'hull_plating'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Consumables - Purple theme
|
||||||
|
const consumables = [
|
||||||
|
'health_pack',
|
||||||
|
'energy_boost',
|
||||||
|
'shield_recharge',
|
||||||
|
'speed_boost',
|
||||||
|
'repair_kit',
|
||||||
|
'ammo_pack',
|
||||||
|
'stealth_device',
|
||||||
|
'scanner_boost',
|
||||||
|
'experience_boost',
|
||||||
|
'credit_multiplier',
|
||||||
|
'lucky_charm',
|
||||||
|
'emergency_beacon'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Cosmetics - Gold theme
|
||||||
|
const cosmetics = [
|
||||||
|
'cool_paint_job',
|
||||||
|
'neon_lights',
|
||||||
|
'custom_decal',
|
||||||
|
'golden_trim',
|
||||||
|
'carbon_fiber',
|
||||||
|
'chrome_finish',
|
||||||
|
'matte_black',
|
||||||
|
'camo_pattern',
|
||||||
|
'flame_design',
|
||||||
|
'electric_aura',
|
||||||
|
'ice_effect',
|
||||||
|
'rainbow_sparkle'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate all images
|
||||||
|
console.log('Generating ship images...');
|
||||||
|
ships.forEach(shipId => {
|
||||||
|
const rarity = shipId.split('_').pop();
|
||||||
|
const color = rarity === 'common' ? '#3498db' : rarity === 'uncommon' ? '#2ecc71' : '#f39c12';
|
||||||
|
const svg = generateSVG(80, 80, color, shipId.replace(/_/g, ' ').toUpperCase(), shipId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/ships/${shipId}.png`), svg);
|
||||||
|
console.log(`Created: ${shipId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Generating weapon images...');
|
||||||
|
weapons.forEach(weaponId => {
|
||||||
|
const rarity = weaponId.split('_').pop();
|
||||||
|
const color = rarity === 'common' ? '#e74c3c' : rarity === 'uncommon' ? '#c0392b' : '#8e44ad';
|
||||||
|
const svg = generateSVG(64, 64, color, weaponId.replace(/_/g, ' ').toUpperCase(), weaponId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/weapons/${weaponId}.png`), svg);
|
||||||
|
console.log(`Created: ${weaponId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Generating armor images...');
|
||||||
|
armors.forEach(armorId => {
|
||||||
|
const rarity = armorId.split('_').pop();
|
||||||
|
const color = rarity === 'common' ? '#27ae60' : rarity === 'uncommon' ? '#16a085' : '#2c3e50';
|
||||||
|
const svg = generateSVG(64, 64, color, armorId.replace(/_/g, ' ').toUpperCase(), armorId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/armors/${armorId}.png`), svg);
|
||||||
|
console.log(`Created: ${armorId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Generating material images...');
|
||||||
|
materials.forEach(materialId => {
|
||||||
|
const svg = generateSVG(48, 48, '#7f8c8d', materialId.replace(/_/g, ' ').toUpperCase(), materialId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/items/materials/${materialId}.png`), svg);
|
||||||
|
console.log(`Created: ${materialId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Generating consumable images...');
|
||||||
|
consumables.forEach(consumableId => {
|
||||||
|
const svg = generateSVG(48, 48, '#9b59b6', consumableId.replace(/_/g, ' ').toUpperCase(), consumableId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/items/consumables/${consumableId}.png`), svg);
|
||||||
|
console.log(`Created: ${consumableId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Generating cosmetic images...');
|
||||||
|
cosmetics.forEach(cosmeticId => {
|
||||||
|
const svg = generateSVG(64, 64, '#f1c40f', cosmeticId.replace(/_/g, ' ').toUpperCase(), cosmeticId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/items/cosmetics/${cosmeticId}.png`), svg);
|
||||||
|
console.log(`Created: ${cosmeticId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate UI placeholder
|
||||||
|
console.log('Generating UI placeholder...');
|
||||||
|
const placeholderSvg = generateSVG(80, 80, '#95a5a6', 'PLACEHOLDER', 'placeholder');
|
||||||
|
fs.writeFileSync(path.join(__dirname, 'assets/images/ui/placeholder.png'), placeholderSvg);
|
||||||
|
|
||||||
|
// Generate some UI icons
|
||||||
|
const icons = ['coin', 'gem', 'heart', 'shield', 'star', 'lock', 'unlock', 'settings'];
|
||||||
|
icons.forEach(iconId => {
|
||||||
|
const svg = generateSVG(32, 32, '#34495e', iconId.toUpperCase(), iconId);
|
||||||
|
fs.writeFileSync(path.join(__dirname, `assets/images/ui/icons/${iconId}.png`), svg);
|
||||||
|
console.log(`Created icon: ${iconId}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n🎉 All placeholder images generated successfully!');
|
||||||
|
console.log('📁 Total images created:');
|
||||||
|
console.log(` Ships: ${ships.length}`);
|
||||||
|
console.log(` Weapons: ${weapons.length}`);
|
||||||
|
console.log(` Armors: ${armors.length}`);
|
||||||
|
console.log(` Materials: ${materials.length}`);
|
||||||
|
console.log(` Consumables: ${consumables.length}`);
|
||||||
|
console.log(` Cosmetics: ${cosmetics.length}`);
|
||||||
|
console.log(` UI Icons: ${icons.length + 1} (including placeholder)`);
|
||||||
|
console.log('\n🚀 Your shop should now display all items with placeholder images!');
|
||||||
@ -10,6 +10,8 @@ const cors = require('cors');
|
|||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const compression = require('compression');
|
const compression = require('compression');
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
const logger = require('./utils/logger');
|
const logger = require('./utils/logger');
|
||||||
@ -32,6 +34,10 @@ const craftingSystem = new CraftingSystem();
|
|||||||
const idleSystem = new IdleSystem();
|
const idleSystem = new IdleSystem();
|
||||||
const itemSystem = new ItemSystem();
|
const itemSystem = new ItemSystem();
|
||||||
|
|
||||||
|
// Set server URL for ItemSystem
|
||||||
|
const SERVER_URL = process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online';
|
||||||
|
itemSystem.setServerUrl(SERVER_URL);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const io = socketIo(server, {
|
const io = socketIo(server, {
|
||||||
@ -69,6 +75,81 @@ app.use(cors({
|
|||||||
credentials: true
|
credentials: true
|
||||||
}));
|
}));
|
||||||
app.use(express.json({ limit: '10mb' }));
|
app.use(express.json({ limit: '10mb' }));
|
||||||
|
|
||||||
|
// Serve static files from the client directory
|
||||||
|
app.use(express.static(path.join(__dirname, '../Client')));
|
||||||
|
|
||||||
|
// Serve ships from server-side storage
|
||||||
|
app.use('/images/ships', (req, res, next) => {
|
||||||
|
const requestedPath = req.path;
|
||||||
|
const serverImagePath = path.join(__dirname, 'assets/images/ships', requestedPath);
|
||||||
|
|
||||||
|
console.log('[IMAGE SERVER] Ship requested:', requestedPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(serverImagePath)) {
|
||||||
|
res.sendFile(serverImagePath);
|
||||||
|
} else {
|
||||||
|
const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
|
||||||
|
res.sendFile(placeholderPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve weapons from server-side storage
|
||||||
|
app.use('/images/weapons', (req, res, next) => {
|
||||||
|
const requestedPath = req.path;
|
||||||
|
const serverImagePath = path.join(__dirname, 'assets/images/weapons', requestedPath);
|
||||||
|
|
||||||
|
console.log('[IMAGE SERVER] Weapon requested:', requestedPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(serverImagePath)) {
|
||||||
|
res.sendFile(serverImagePath);
|
||||||
|
} else {
|
||||||
|
const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
|
||||||
|
res.sendFile(placeholderPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve armors from server-side storage
|
||||||
|
app.use('/images/armors', (req, res, next) => {
|
||||||
|
const requestedPath = req.path;
|
||||||
|
const serverImagePath = path.join(__dirname, 'assets/images/armors', requestedPath);
|
||||||
|
|
||||||
|
console.log('[IMAGE SERVER] Armor requested:', requestedPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(serverImagePath)) {
|
||||||
|
res.sendFile(serverImagePath);
|
||||||
|
} else {
|
||||||
|
const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
|
||||||
|
res.sendFile(placeholderPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve other items (materials, consumables, cosmetics) from server-side storage
|
||||||
|
app.use('/images/items', (req, res, next) => {
|
||||||
|
const requestedPath = req.path;
|
||||||
|
const serverImagePath = path.join(__dirname, 'assets/images/items', requestedPath);
|
||||||
|
|
||||||
|
console.log('[IMAGE SERVER] Item requested:', requestedPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(serverImagePath)) {
|
||||||
|
res.sendFile(serverImagePath);
|
||||||
|
} else {
|
||||||
|
const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
|
||||||
|
res.sendFile(placeholderPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve UI elements and icons from server-side storage
|
||||||
|
app.use('/images/ui', (req, res, next) => {
|
||||||
|
const requestedPath = req.path;
|
||||||
|
const serverImagePath = path.join(__dirname, 'assets/images/ui', requestedPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(serverImagePath)) {
|
||||||
|
res.sendFile(serverImagePath);
|
||||||
|
} else {
|
||||||
|
res.status(404).send('UI asset not found');
|
||||||
|
}
|
||||||
|
});
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
@ -96,7 +177,7 @@ app.get('/api/ssc/version', (req, res) => {
|
|||||||
// Shop API endpoints
|
// Shop API endpoints
|
||||||
app.get('/api/shop/items', (req, res) => {
|
app.get('/api/shop/items', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const shopItems = itemSystem.getShopItems();
|
const shopItems = itemSystem.getRandomShopItems();
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
items: shopItems,
|
items: shopItems,
|
||||||
@ -114,19 +195,19 @@ app.get('/api/shop/items', (req, res) => {
|
|||||||
app.get('/api/shop/items/:category', (req, res) => {
|
app.get('/api/shop/items/:category', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { category } = req.params;
|
const { category } = req.params;
|
||||||
const items = itemSystem.getItemsByCategory(category);
|
const items = itemSystem.getItemsByType(category);
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
category: category,
|
|
||||||
items: items,
|
items: items,
|
||||||
|
category: category,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[GAME SERVER] Error fetching shop category:', error);
|
console.error('[GAME SERVER] Error fetching category items:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to fetch shop category'
|
error: 'Failed to fetch category items'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -134,7 +215,15 @@ app.get('/api/shop/items/:category', (req, res) => {
|
|||||||
app.get('/api/items/:itemId', (req, res) => {
|
app.get('/api/items/:itemId', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { itemId } = req.params;
|
const { itemId } = req.params;
|
||||||
const item = itemSystem.getItem(itemId);
|
|
||||||
|
// Find item across all categories
|
||||||
|
const allItems = itemSystem.getAllItems();
|
||||||
|
let item = null;
|
||||||
|
|
||||||
|
for (const [category, items] of Object.entries(allItems)) {
|
||||||
|
item = items.find(i => i.id === itemId);
|
||||||
|
if (item) break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
@ -145,13 +234,14 @@ app.get('/api/items/:itemId', (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
item: item
|
item: item,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[GAME SERVER] Error fetching item:', error);
|
console.error('[GAME SERVER] Error fetching item details:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to fetch item'
|
error: 'Failed to fetch item details'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -371,17 +461,27 @@ io.on('connection', (socket) => {
|
|||||||
|
|
||||||
// Shop and item system events
|
// Shop and item system events
|
||||||
socket.on('getShopItems', (data) => {
|
socket.on('getShopItems', (data) => {
|
||||||
console.log('[GAME SERVER] Sending shop items to:', socket.id);
|
console.log('[GAME SERVER] getShopItems request received from:', socket.id);
|
||||||
|
console.log('[GAME SERVER] Request data:', data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const shopItems = itemSystem.getShopItems();
|
console.log('[GAME SERVER] Getting shop items from ItemSystem...');
|
||||||
socket.emit('shopItemsReceived', {
|
const shopItems = itemSystem.getRandomShopItems();
|
||||||
|
console.log('[GAME SERVER] Got shop items:', shopItems.length, 'items');
|
||||||
|
console.log('[GAME SERVER] Sample item:', shopItems[0]);
|
||||||
|
|
||||||
|
const response = {
|
||||||
success: true,
|
success: true,
|
||||||
items: shopItems,
|
items: shopItems,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
console.log('[GAME SERVER] Sending response:', response);
|
||||||
|
socket.emit('shopItemsReceived', response);
|
||||||
|
console.log('[GAME SERVER] Response sent successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[GAME SERVER] Error sending shop items:', error);
|
console.error('[GAME SERVER] Error sending shop items:', error);
|
||||||
|
console.error('[GAME SERVER] Error stack:', error.stack);
|
||||||
socket.emit('shopItemsReceived', {
|
socket.emit('shopItemsReceived', {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to load shop items'
|
error: 'Failed to load shop items'
|
||||||
@ -726,13 +826,7 @@ io.on('connection', (socket) => {
|
|||||||
socket.on('get_dungeons', () => {
|
socket.on('get_dungeons', () => {
|
||||||
console.log('[GAME SERVER] Sending dungeons data to:', socket.id);
|
console.log('[GAME SERVER] Sending dungeons data to:', socket.id);
|
||||||
const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty();
|
const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty();
|
||||||
socket.emit('dungeons_data', { dungeons });
|
socket.emit('dungeons_data', dungeons);
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('get_room_types', () => {
|
|
||||||
console.log('[GAME SERVER] Sending room types to:', socket.id);
|
|
||||||
const roomTypes = dungeonSystem.getRoomTypes();
|
|
||||||
socket.emit('room_types_data', roomTypes);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('get_enemy_templates', () => {
|
socket.on('get_enemy_templates', () => {
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
class ItemSystem {
|
class ItemSystem {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
// Server configuration - will be set dynamically
|
||||||
|
this.serverUrl = process.env.SERVER_URL || 'http://localhost:3002';
|
||||||
|
|
||||||
// Master item catalog - single source of truth
|
// Master item catalog - single source of truth
|
||||||
this.itemCatalog = {
|
this.itemCatalog = {
|
||||||
// Ships
|
// Ships
|
||||||
@ -17,7 +20,7 @@ class ItemSystem {
|
|||||||
price: 5000,
|
price: 5000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Reliable starter cruiser for new pilots',
|
description: 'Reliable starter cruiser for new pilots',
|
||||||
texture: 'assets/textures/ships/starter_cruiser.png',
|
texturePath: 'images/ships/starter_cruiser_common.png',
|
||||||
stats: { attack: 15, speed: 10, defense: 12, hull: 100 },
|
stats: { attack: 15, speed: 10, defense: 12, hull: 100 },
|
||||||
categories: ['shop', 'dungeon_reward'],
|
categories: ['shop', 'dungeon_reward'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
@ -31,7 +34,7 @@ class ItemSystem {
|
|||||||
price: 12000,
|
price: 12000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Upgraded starter cruiser with enhanced systems',
|
description: 'Upgraded starter cruiser with enhanced systems',
|
||||||
texture: 'assets/textures/ships/starter_cruiser.png',
|
texturePath: 'images/ships/starter_cruiser_uncommon.png',
|
||||||
stats: { attack: 18, speed: 12, defense: 15, hull: 120 },
|
stats: { attack: 18, speed: 12, defense: 15, hull: 120 },
|
||||||
categories: ['shop', 'dungeon_reward'],
|
categories: ['shop', 'dungeon_reward'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
@ -45,7 +48,7 @@ class ItemSystem {
|
|||||||
price: 25000,
|
price: 25000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Elite starter cruiser with maximum upgrades',
|
description: 'Elite starter cruiser with maximum upgrades',
|
||||||
texture: 'assets/textures/ships/starter_cruiser.png',
|
texturePath: 'images/ships/starter_cruiser_rare.png',
|
||||||
stats: { attack: 22, speed: 15, defense: 18, hull: 150 },
|
stats: { attack: 22, speed: 15, defense: 18, hull: 150 },
|
||||||
categories: ['shop', 'dungeon_reward'],
|
categories: ['shop', 'dungeon_reward'],
|
||||||
requirements: { level: 10 },
|
requirements: { level: 10 },
|
||||||
@ -59,7 +62,7 @@ class ItemSystem {
|
|||||||
price: 8000,
|
price: 8000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Fast attack ship for hit-and-run tactics',
|
description: 'Fast attack ship for hit-and-run tactics',
|
||||||
texture: 'assets/textures/ships/interceptor.png',
|
texturePath: 'images/ships/interceptor_common.png',
|
||||||
stats: { attack: 12, speed: 18, defense: 8, hull: 80 },
|
stats: { attack: 12, speed: 18, defense: 8, hull: 80 },
|
||||||
categories: ['shop', 'dungeon_reward'],
|
categories: ['shop', 'dungeon_reward'],
|
||||||
requirements: { level: 3 },
|
requirements: { level: 3 },
|
||||||
@ -73,7 +76,7 @@ class ItemSystem {
|
|||||||
price: 18000,
|
price: 18000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Enhanced interceptor with improved weapons',
|
description: 'Enhanced interceptor with improved weapons',
|
||||||
texture: 'assets/textures/ships/interceptor.png',
|
texturePath: 'images/ships/interceptor_uncommon.png',
|
||||||
stats: { attack: 15, speed: 22, defense: 10, hull: 95 },
|
stats: { attack: 15, speed: 22, defense: 10, hull: 95 },
|
||||||
categories: ['shop', 'dungeon_reward'],
|
categories: ['shop', 'dungeon_reward'],
|
||||||
requirements: { level: 7 },
|
requirements: { level: 7 },
|
||||||
@ -91,6 +94,7 @@ class ItemSystem {
|
|||||||
price: 100,
|
price: 100,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Basic armor material for ship upgrades',
|
description: 'Basic armor material for ship upgrades',
|
||||||
|
texture: 'http://localhost:3002/images/items/materials/steel_plating.png',
|
||||||
categories: ['shop', 'dungeon_loot', 'crafting'],
|
categories: ['shop', 'dungeon_loot', 'crafting'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -105,6 +109,7 @@ class ItemSystem {
|
|||||||
price: 250,
|
price: 250,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Power source for advanced upgrades',
|
description: 'Power source for advanced upgrades',
|
||||||
|
texture: 'http://localhost:3002/images/items/materials/energy_crystal.png',
|
||||||
categories: ['shop', 'dungeon_loot', 'crafting'],
|
categories: ['shop', 'dungeon_loot', 'crafting'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -119,6 +124,7 @@ class ItemSystem {
|
|||||||
price: 500,
|
price: 500,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Advanced fuel for long-range travel',
|
description: 'Advanced fuel for long-range travel',
|
||||||
|
texture: 'http://localhost:3002/images/items/materials/quantum_core.png',
|
||||||
categories: ['shop', 'dungeon_loot', 'crafting'],
|
categories: ['shop', 'dungeon_loot', 'crafting'],
|
||||||
requirements: { level: 10 },
|
requirements: { level: 10 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -133,6 +139,7 @@ class ItemSystem {
|
|||||||
price: 2000,
|
price: 2000,
|
||||||
currency: 'gems',
|
currency: 'gems',
|
||||||
description: 'Exotic matter for ultimate upgrades',
|
description: 'Exotic matter for ultimate upgrades',
|
||||||
|
texture: 'http://localhost:3002/images/items/materials/dark_matter_fragment.png',
|
||||||
categories: ['shop', 'dungeon_loot', 'crafting'],
|
categories: ['shop', 'dungeon_loot', 'crafting'],
|
||||||
requirements: { level: 15 },
|
requirements: { level: 15 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -151,6 +158,7 @@ class ItemSystem {
|
|||||||
price: 50,
|
price: 50,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Instantly repairs ship damage',
|
description: 'Instantly repairs ship damage',
|
||||||
|
texture: 'http://localhost:3002/images/items/consumables/repair_kit.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -167,6 +175,7 @@ class ItemSystem {
|
|||||||
price: 75,
|
price: 75,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Temporary energy increase',
|
description: 'Temporary energy increase',
|
||||||
|
texture: 'http://localhost:3002/images/items/consumables/energy_boost.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -183,6 +192,7 @@ class ItemSystem {
|
|||||||
price: 150,
|
price: 150,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Temporary shield enhancement',
|
description: 'Temporary shield enhancement',
|
||||||
|
texture: 'http://localhost:3002/images/items/consumables/shield_recharge.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -199,6 +209,7 @@ class ItemSystem {
|
|||||||
price: 300,
|
price: 300,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Increases damage output temporarily',
|
description: 'Increases damage output temporarily',
|
||||||
|
texture: 'http://localhost:3002/images/items/consumables/ammo_pack.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 8 },
|
requirements: { level: 8 },
|
||||||
stackable: true,
|
stackable: true,
|
||||||
@ -219,6 +230,7 @@ class ItemSystem {
|
|||||||
price: 500,
|
price: 500,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Red color scheme for your ship',
|
description: 'Red color scheme for your ship',
|
||||||
|
texture: 'http://localhost:3002/images/items/cosmetics/cool_paint_job.png',
|
||||||
categories: ['shop'],
|
categories: ['shop'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -233,6 +245,7 @@ class ItemSystem {
|
|||||||
price: 500,
|
price: 500,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Blue color scheme for your ship',
|
description: 'Blue color scheme for your ship',
|
||||||
|
texture: 'http://localhost:3002/images/items/cosmetics/neon_lights.png',
|
||||||
categories: ['shop'],
|
categories: ['shop'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -247,6 +260,7 @@ class ItemSystem {
|
|||||||
price: 1000,
|
price: 1000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Gold accent trim for your ship',
|
description: 'Gold accent trim for your ship',
|
||||||
|
texture: 'http://localhost:3002/images/items/cosmetics/golden_trim.png',
|
||||||
categories: ['shop'],
|
categories: ['shop'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -261,6 +275,7 @@ class ItemSystem {
|
|||||||
price: 2500,
|
price: 2500,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Colorful engine trail effect',
|
description: 'Colorful engine trail effect',
|
||||||
|
texture: 'http://localhost:3002/images/items/cosmetics/custom_decal.png',
|
||||||
categories: ['shop'],
|
categories: ['shop'],
|
||||||
requirements: { level: 10 },
|
requirements: { level: 10 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -279,6 +294,7 @@ class ItemSystem {
|
|||||||
price: 1000,
|
price: 1000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Basic laser weapon for beginners',
|
description: 'Basic laser weapon for beginners',
|
||||||
|
texture: 'http://localhost:3002/images/weapons/laser_pistol_common.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -292,6 +308,7 @@ class ItemSystem {
|
|||||||
price: 2500,
|
price: 2500,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Plasma-based weapon with increased damage',
|
description: 'Plasma-based weapon with increased damage',
|
||||||
|
texture: 'http://localhost:3002/images/weapons/plasma_rifle_uncommon.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -305,6 +322,7 @@ class ItemSystem {
|
|||||||
price: 6000,
|
price: 6000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Advanced quantum weapon with high damage output',
|
description: 'Advanced quantum weapon with high damage output',
|
||||||
|
texture: 'http://localhost:3002/images/weapons/plasma_rifle_rare.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 10 },
|
requirements: { level: 10 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -322,6 +340,7 @@ class ItemSystem {
|
|||||||
price: 800,
|
price: 800,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Basic shield protection for beginners',
|
description: 'Basic shield protection for beginners',
|
||||||
|
texture: 'http://localhost:3002/images/armors/light_armor_common.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 1 },
|
requirements: { level: 1 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -335,6 +354,7 @@ class ItemSystem {
|
|||||||
price: 2000,
|
price: 2000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Energy-based armor with enhanced protection',
|
description: 'Energy-based armor with enhanced protection',
|
||||||
|
texture: 'http://localhost:3002/images/armors/light_armor_uncommon.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 5 },
|
requirements: { level: 5 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -348,6 +368,7 @@ class ItemSystem {
|
|||||||
price: 5000,
|
price: 5000,
|
||||||
currency: 'credits',
|
currency: 'credits',
|
||||||
description: 'Advanced quantum armor with maximum protection',
|
description: 'Advanced quantum armor with maximum protection',
|
||||||
|
texture: 'http://localhost:3002/images/armors/light_armor_rare.png',
|
||||||
categories: ['shop', 'dungeon_loot'],
|
categories: ['shop', 'dungeon_loot'],
|
||||||
requirements: { level: 10 },
|
requirements: { level: 10 },
|
||||||
stackable: false,
|
stackable: false,
|
||||||
@ -613,6 +634,76 @@ class ItemSystem {
|
|||||||
|
|
||||||
return { valid: errors.length === 0, errors };
|
return { valid: errors.length === 0, errors };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set server URL dynamically
|
||||||
|
*/
|
||||||
|
setServerUrl(url) {
|
||||||
|
this.serverUrl = url;
|
||||||
|
console.log(`[ITEM SYSTEM] Server URL set to: ${this.serverUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process item to add full texture URL
|
||||||
|
*/
|
||||||
|
processItem(item) {
|
||||||
|
const processedItem = { ...item };
|
||||||
|
|
||||||
|
// Add full texture URL if texturePath exists
|
||||||
|
if (item.texturePath) {
|
||||||
|
processedItem.texture = `${this.serverUrl}/${item.texturePath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to old texture field if exists
|
||||||
|
else if (item.texture) {
|
||||||
|
processedItem.texture = item.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default placeholder
|
||||||
|
else {
|
||||||
|
processedItem.texture = `${this.serverUrl}/images/ui/placeholder.png`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items with processed URLs
|
||||||
|
*/
|
||||||
|
getItemsByType(type) {
|
||||||
|
const items = this.itemCatalog[type] || [];
|
||||||
|
return items.map(item => this.processItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all items with processed URLs
|
||||||
|
*/
|
||||||
|
getAllItems() {
|
||||||
|
const allItems = {};
|
||||||
|
|
||||||
|
Object.keys(this.itemCatalog).forEach(type => {
|
||||||
|
allItems[type] = this.getItemsByType(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return allItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get random shop items with processed URLs
|
||||||
|
*/
|
||||||
|
getRandomShopItems(count = 8) {
|
||||||
|
const allItems = Object.values(this.itemCatalog).flat();
|
||||||
|
const selectedItems = [];
|
||||||
|
|
||||||
|
// Randomly select items
|
||||||
|
const shuffled = [...allItems].sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(count, shuffled.length); i++) {
|
||||||
|
selectedItems.push(this.processItem(shuffled[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedItems;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ItemSystem;
|
module.exports = ItemSystem;
|
||||||
|
|||||||