moved the items call from the client to the server

This commit is contained in:
Robert MacRae 2026-01-26 18:46:53 -04:00
parent b793b85e25
commit 6db6e1ebdb
41 changed files with 725 additions and 50 deletions

View File

@ -14,7 +14,7 @@ class GameInitializer {
this.currentUser = null;
this.socket = null;
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);
}
@ -74,6 +74,7 @@ class GameInitializer {
const FORCED_URL = 'https://dev.gameserver.galaxystrike.online';
console.log('[GAME INITIALIZER] FORCING URL to:', FORCED_URL);
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
this.socket = io(FORCED_URL, {

View File

@ -547,25 +547,36 @@ class Economy {
const canAfford = this.canAfford(item.price, item.currency);
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 `
<div class="shop-item ${canAfford ? '' : 'cant-afford'} ${isOwned ? 'owned' : ''}" data-item-id="${item.id}">
<div class="shop-item-content">
<div class="shop-item-header">
<h3 class="shop-item-name">${item.name}</h3>
<span class="shop-item-rarity ${item.rarity}">${item.rarity}</span>
<div class="shop-item-image">
<img src="${imageUrl}" alt="${item.name}"
onerror="this.src='${placeholderUrl}'"
loading="lazy">
</div>
<div class="shop-item-body">
<p class="shop-item-description">${item.description}</p>
<div class="shop-item-price">
${this.formatPrice(item)}
<div class="shop-item-info">
<div class="shop-item-header">
<h3 class="shop-item-name">${item.name}</h3>
<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 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>
@ -582,6 +593,56 @@ class Economy {
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() {
return {

View File

@ -123,9 +123,13 @@ class ItemSystem {
clearTimeout(timeout);
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) {
console.log('[ITEM SYSTEM] Successfully received', data.items?.length || 0, 'items');
console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp);
console.log('[ITEM SYSTEM] Sample item:', data.items?.[0]);
resolve(data.items || []);
} else {
console.error('[ITEM SYSTEM] Server returned error:', data.error);

View File

@ -61,6 +61,22 @@ class ShipSystem {
});
}
/**
* 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) {
const card = document.createElement('div');
card.className = `ship-card ${ship.status === 'active' ? 'active' : ''}`;
@ -68,17 +84,13 @@ class ShipSystem {
card.innerHTML = `
<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-rarity ${ship.rarity.toLowerCase()}">${ship.rarity}</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;
@ -106,10 +118,22 @@ class ShipSystem {
const ship = player.ship;
if (elements.currentShipImage) {
// Use the ship's texture if available, otherwise fallback
const imagePath = ship.texture || `assets/textures/ships/starter_cruiser.png`;
// Use server image for multiplayer, local for singleplayer
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.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.currentShipClass) elements.currentShipClass.textContent = ship.class || 'Unknown';

View File

@ -24,7 +24,7 @@ class LiveMainMenu {
this.currentUser = null;
this.servers = []; // Renamed from serverList to avoid conflict with DOM element
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.isLocalMode = false; // Track if we're in local mode

View File

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

View File

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 254 KiB

View File

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 226 KiB

View File

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

View File

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 213 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

View File

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 324 KiB

View File

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View File

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View File

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 312 KiB

View File

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View File

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

View File

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

View File

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

View File

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

View File

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 185 KiB

View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

View File

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 140 KiB

View 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.');

View 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!');

View File

@ -10,6 +10,8 @@ const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const mongoose = require('mongoose');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const logger = require('./utils/logger');
@ -32,6 +34,10 @@ const craftingSystem = new CraftingSystem();
const idleSystem = new IdleSystem();
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 server = http.createServer(app);
const io = socketIo(server, {
@ -69,6 +75,81 @@ app.use(cors({
credentials: true
}));
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 }));
// Health check endpoint
@ -96,7 +177,7 @@ app.get('/api/ssc/version', (req, res) => {
// Shop API endpoints
app.get('/api/shop/items', (req, res) => {
try {
const shopItems = itemSystem.getShopItems();
const shopItems = itemSystem.getRandomShopItems();
res.status(200).json({
success: true,
items: shopItems,
@ -114,19 +195,19 @@ app.get('/api/shop/items', (req, res) => {
app.get('/api/shop/items/:category', (req, res) => {
try {
const { category } = req.params;
const items = itemSystem.getItemsByCategory(category);
const items = itemSystem.getItemsByType(category);
res.status(200).json({
success: true,
category: category,
items: items,
category: category,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('[GAME SERVER] Error fetching shop category:', error);
console.error('[GAME SERVER] Error fetching category items:', error);
res.status(500).json({
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) => {
try {
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) {
return res.status(404).json({
@ -145,13 +234,14 @@ app.get('/api/items/:itemId', (req, res) => {
res.status(200).json({
success: true,
item: item
item: item,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('[GAME SERVER] Error fetching item:', error);
console.error('[GAME SERVER] Error fetching item details:', error);
res.status(500).json({
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
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 {
const shopItems = itemSystem.getShopItems();
socket.emit('shopItemsReceived', {
console.log('[GAME SERVER] Getting shop items from ItemSystem...');
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,
items: shopItems,
timestamp: new Date().toISOString()
});
};
console.log('[GAME SERVER] Sending response:', response);
socket.emit('shopItemsReceived', response);
console.log('[GAME SERVER] Response sent successfully');
} catch (error) {
console.error('[GAME SERVER] Error sending shop items:', error);
console.error('[GAME SERVER] Error stack:', error.stack);
socket.emit('shopItemsReceived', {
success: false,
error: 'Failed to load shop items'
@ -726,13 +826,7 @@ io.on('connection', (socket) => {
socket.on('get_dungeons', () => {
console.log('[GAME SERVER] Sending dungeons data to:', socket.id);
const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty();
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.emit('dungeons_data', dungeons);
});
socket.on('get_enemy_templates', () => {

View File

@ -5,6 +5,9 @@
class ItemSystem {
constructor() {
// Server configuration - will be set dynamically
this.serverUrl = process.env.SERVER_URL || 'http://localhost:3002';
// Master item catalog - single source of truth
this.itemCatalog = {
// Ships
@ -17,7 +20,7 @@ class ItemSystem {
price: 5000,
currency: 'credits',
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 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 1 },
@ -31,7 +34,7 @@ class ItemSystem {
price: 12000,
currency: 'credits',
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 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 5 },
@ -45,7 +48,7 @@ class ItemSystem {
price: 25000,
currency: 'credits',
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 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 10 },
@ -59,7 +62,7 @@ class ItemSystem {
price: 8000,
currency: 'credits',
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 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 3 },
@ -73,7 +76,7 @@ class ItemSystem {
price: 18000,
currency: 'credits',
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 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 7 },
@ -91,6 +94,7 @@ class ItemSystem {
price: 100,
currency: 'credits',
description: 'Basic armor material for ship upgrades',
texture: 'http://localhost:3002/images/items/materials/steel_plating.png',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 1 },
stackable: true,
@ -105,6 +109,7 @@ class ItemSystem {
price: 250,
currency: 'credits',
description: 'Power source for advanced upgrades',
texture: 'http://localhost:3002/images/items/materials/energy_crystal.png',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 5 },
stackable: true,
@ -119,6 +124,7 @@ class ItemSystem {
price: 500,
currency: 'credits',
description: 'Advanced fuel for long-range travel',
texture: 'http://localhost:3002/images/items/materials/quantum_core.png',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 10 },
stackable: true,
@ -133,6 +139,7 @@ class ItemSystem {
price: 2000,
currency: 'gems',
description: 'Exotic matter for ultimate upgrades',
texture: 'http://localhost:3002/images/items/materials/dark_matter_fragment.png',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 15 },
stackable: true,
@ -151,6 +158,7 @@ class ItemSystem {
price: 50,
currency: 'credits',
description: 'Instantly repairs ship damage',
texture: 'http://localhost:3002/images/items/consumables/repair_kit.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: true,
@ -167,6 +175,7 @@ class ItemSystem {
price: 75,
currency: 'credits',
description: 'Temporary energy increase',
texture: 'http://localhost:3002/images/items/consumables/energy_boost.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: true,
@ -183,6 +192,7 @@ class ItemSystem {
price: 150,
currency: 'credits',
description: 'Temporary shield enhancement',
texture: 'http://localhost:3002/images/items/consumables/shield_recharge.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: true,
@ -199,6 +209,7 @@ class ItemSystem {
price: 300,
currency: 'credits',
description: 'Increases damage output temporarily',
texture: 'http://localhost:3002/images/items/consumables/ammo_pack.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 8 },
stackable: true,
@ -219,6 +230,7 @@ class ItemSystem {
price: 500,
currency: 'credits',
description: 'Red color scheme for your ship',
texture: 'http://localhost:3002/images/items/cosmetics/cool_paint_job.png',
categories: ['shop'],
requirements: { level: 1 },
stackable: false,
@ -233,6 +245,7 @@ class ItemSystem {
price: 500,
currency: 'credits',
description: 'Blue color scheme for your ship',
texture: 'http://localhost:3002/images/items/cosmetics/neon_lights.png',
categories: ['shop'],
requirements: { level: 1 },
stackable: false,
@ -247,6 +260,7 @@ class ItemSystem {
price: 1000,
currency: 'credits',
description: 'Gold accent trim for your ship',
texture: 'http://localhost:3002/images/items/cosmetics/golden_trim.png',
categories: ['shop'],
requirements: { level: 5 },
stackable: false,
@ -261,6 +275,7 @@ class ItemSystem {
price: 2500,
currency: 'credits',
description: 'Colorful engine trail effect',
texture: 'http://localhost:3002/images/items/cosmetics/custom_decal.png',
categories: ['shop'],
requirements: { level: 10 },
stackable: false,
@ -279,6 +294,7 @@ class ItemSystem {
price: 1000,
currency: 'credits',
description: 'Basic laser weapon for beginners',
texture: 'http://localhost:3002/images/weapons/laser_pistol_common.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: false,
@ -292,6 +308,7 @@ class ItemSystem {
price: 2500,
currency: 'credits',
description: 'Plasma-based weapon with increased damage',
texture: 'http://localhost:3002/images/weapons/plasma_rifle_uncommon.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: false,
@ -305,6 +322,7 @@ class ItemSystem {
price: 6000,
currency: 'credits',
description: 'Advanced quantum weapon with high damage output',
texture: 'http://localhost:3002/images/weapons/plasma_rifle_rare.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 10 },
stackable: false,
@ -322,6 +340,7 @@ class ItemSystem {
price: 800,
currency: 'credits',
description: 'Basic shield protection for beginners',
texture: 'http://localhost:3002/images/armors/light_armor_common.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: false,
@ -335,6 +354,7 @@ class ItemSystem {
price: 2000,
currency: 'credits',
description: 'Energy-based armor with enhanced protection',
texture: 'http://localhost:3002/images/armors/light_armor_uncommon.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: false,
@ -348,6 +368,7 @@ class ItemSystem {
price: 5000,
currency: 'credits',
description: 'Advanced quantum armor with maximum protection',
texture: 'http://localhost:3002/images/armors/light_armor_rare.png',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 10 },
stackable: false,
@ -613,6 +634,76 @@ class ItemSystem {
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;