384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
/**
|
|
* Galaxy Strike Online - Client Item System
|
|
* Dynamically loads and manages items from the GameServer
|
|
*/
|
|
|
|
class ItemSystem {
|
|
constructor(gameEngine) {
|
|
this.game = gameEngine;
|
|
|
|
// Item storage
|
|
this.itemCatalog = new Map(); // itemId -> item data
|
|
this.shopItems = []; // Array of shop items
|
|
this.lastUpdated = null;
|
|
|
|
// Loading state
|
|
this.isLoading = false;
|
|
this.loadPromise = null;
|
|
|
|
// Event listeners
|
|
this.eventListeners = new Map();
|
|
}
|
|
|
|
/**
|
|
* Initialize the item system and load data from server
|
|
*/
|
|
async initialize() {
|
|
console.log('[ITEM SYSTEM] Initializing client item system');
|
|
|
|
if (this.loadPromise) {
|
|
return this.loadPromise;
|
|
}
|
|
|
|
this.loadPromise = this.loadFromServer();
|
|
return this.loadPromise;
|
|
}
|
|
|
|
/**
|
|
* Load all items from the GameServer
|
|
*/
|
|
async loadFromServer() {
|
|
if (this.isLoading) {
|
|
console.log('[ITEM SYSTEM] Already loading items from server');
|
|
return this.loadPromise;
|
|
}
|
|
|
|
this.isLoading = true;
|
|
|
|
try {
|
|
console.log('[ITEM SYSTEM] Loading items from GameServer - Multiplayer Mode');
|
|
console.log('[ITEM SYSTEM] Socket connection status:', !!window.game?.socket);
|
|
|
|
if (!window.game || !window.game.socket) {
|
|
throw new Error('Not connected to server - multiplayer mode requires server connection');
|
|
}
|
|
|
|
// Load shop items from server
|
|
const shopItems = await this.fetchShopItems();
|
|
|
|
console.log('[ITEM SYSTEM] Received', shopItems.length, 'items from server');
|
|
|
|
// Process and store items
|
|
this.processServerItems(shopItems);
|
|
|
|
this.lastUpdated = Date.now();
|
|
console.log(`[ITEM SYSTEM] Successfully loaded ${this.itemCatalog.size} items from server`);
|
|
console.log('[ITEM SYSTEM] Item categories loaded:', Object.keys(this.itemCatalog).length);
|
|
|
|
// Emit loaded event
|
|
this.emit('itemsLoaded', {
|
|
itemCount: this.itemCatalog.size,
|
|
shopItemCount: this.shopItems.length,
|
|
timestamp: this.lastUpdated
|
|
});
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
console.error('[ITEM SYSTEM] Failed to load items from server:', error);
|
|
console.error('[ITEM SYSTEM] Error details:', {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
socketConnected: !!window.game?.socket,
|
|
socketId: window.game?.socket?.id
|
|
});
|
|
|
|
// No fallback - emit error event
|
|
this.emit('itemsLoadError', error);
|
|
return false;
|
|
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch shop items from the GameServer
|
|
*/
|
|
async fetchShopItems() {
|
|
console.log('[ITEM SYSTEM] Starting fetchShopItems');
|
|
|
|
if (!window.game || !window.game.socket) {
|
|
console.error('[ITEM SYSTEM] No socket connection available');
|
|
throw new Error('Not connected to server');
|
|
}
|
|
|
|
console.log('[ITEM SYSTEM] Socket ID:', window.game.socket.id);
|
|
console.log('[ITEM SYSTEM] Socket connected:', window.game.socket.connected);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const timeout = setTimeout(() => {
|
|
console.error('[ITEM SYSTEM] Server request timeout after 10 seconds');
|
|
window.game.socket.off('shopItemsReceived', handleResponse);
|
|
reject(new Error('Server request timeout'));
|
|
}, 10000);
|
|
|
|
// Request shop items from server
|
|
console.log('[ITEM SYSTEM] Emitting getShopItems request');
|
|
window.game.socket.emit('getShopItems', {});
|
|
|
|
// Listen for response
|
|
const handleResponse = (data) => {
|
|
console.log('[ITEM SYSTEM] Received shopItemsReceived response:', data);
|
|
clearTimeout(timeout);
|
|
window.game.socket.off('shopItemsReceived', handleResponse);
|
|
|
|
if (data.success) {
|
|
console.log('[ITEM SYSTEM] Successfully received', data.items?.length || 0, 'items');
|
|
console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp);
|
|
resolve(data.items || []);
|
|
} else {
|
|
console.error('[ITEM SYSTEM] Server returned error:', data.error);
|
|
reject(new Error(data.error || 'Failed to load shop items'));
|
|
}
|
|
};
|
|
|
|
console.log('[ITEM SYSTEM] Setting up shopItemsReceived listener');
|
|
window.game.socket.on('shopItemsReceived', handleResponse);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch specific item details from server
|
|
*/
|
|
async fetchItemDetails(itemId) {
|
|
if (!window.game || !window.game.socket) {
|
|
throw new Error('Not connected to server');
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const timeout = setTimeout(() => {
|
|
reject(new Error('Server request timeout'));
|
|
}, 5000);
|
|
|
|
// Request item details from server
|
|
window.game.socket.emit('getItemDetails', { itemId });
|
|
|
|
// Listen for response
|
|
const handleResponse = (data) => {
|
|
clearTimeout(timeout);
|
|
window.game.socket.off('itemDetailsReceived', handleResponse);
|
|
|
|
if (data.success) {
|
|
// Cache the item
|
|
this.itemCatalog.set(itemId, data.item);
|
|
resolve(data.item);
|
|
} else {
|
|
reject(new Error(data.error || 'Item not found'));
|
|
}
|
|
};
|
|
|
|
window.game.socket.on('itemDetailsReceived', handleResponse);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Process items received from server
|
|
*/
|
|
processServerItems(items) {
|
|
console.log('[ITEM SYSTEM] Processing', items.length, 'items from server');
|
|
console.log('[ITEM SYSTEM] Sample items:', items.slice(0, 3));
|
|
|
|
this.itemCatalog.clear();
|
|
this.shopItems = [];
|
|
|
|
for (const item of items) {
|
|
// Store in catalog
|
|
this.itemCatalog.set(item.id, item);
|
|
|
|
// Add to shop items if available for shop
|
|
if (item.categories && item.categories.includes('shop')) {
|
|
this.shopItems.push(item);
|
|
}
|
|
|
|
console.log('[ITEM SYSTEM] Added item:', {
|
|
id: item.id,
|
|
name: item.name,
|
|
type: item.type,
|
|
rarity: item.rarity,
|
|
price: item.price,
|
|
categories: item.categories
|
|
});
|
|
}
|
|
|
|
console.log('[ITEM SYSTEM] Processing complete - Catalog:', this.itemCatalog.size, 'Shop items:', this.shopItems.length);
|
|
console.log('[ITEM SYSTEM] Shop items by type:', this.shopItems.reduce((acc, item) => {
|
|
acc[item.type] = (acc[item.type] || 0) + 1;
|
|
return acc;
|
|
}, {}));
|
|
}
|
|
|
|
/**
|
|
* Get item by ID
|
|
*/
|
|
getItem(itemId) {
|
|
// Return from cache if available
|
|
if (this.itemCatalog.has(itemId)) {
|
|
return this.itemCatalog.get(itemId);
|
|
}
|
|
|
|
// Try to fetch from server if not cached
|
|
if (window.game && window.game.socket) {
|
|
this.fetchItemDetails(itemId).catch(error => {
|
|
console.warn(`[ITEM SYSTEM] Failed to fetch item ${itemId}:`, error);
|
|
});
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get all shop items
|
|
*/
|
|
getShopItems() {
|
|
return [...this.shopItems];
|
|
}
|
|
|
|
/**
|
|
* Get items by category
|
|
*/
|
|
getItemsByCategory(category) {
|
|
return Array.from(this.itemCatalog.values()).filter(item =>
|
|
item.type === category || (item.categories && item.categories.includes(category))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get items by type
|
|
*/
|
|
getItemsByType(type) {
|
|
return Array.from(this.itemCatalog.values()).filter(item => item.type === type);
|
|
}
|
|
|
|
/**
|
|
* Get items by rarity
|
|
*/
|
|
getItemsByRarity(rarity) {
|
|
return Array.from(this.itemCatalog.values()).filter(item => item.rarity === rarity);
|
|
}
|
|
|
|
/**
|
|
* Check if player can use item based on requirements
|
|
*/
|
|
canPlayerUseItem(item, playerLevel = null) {
|
|
if (!item.requirements) return true;
|
|
|
|
// Get player level if not provided
|
|
if (playerLevel === null && window.game && window.game.systems && window.game.systems.player) {
|
|
playerLevel = window.game.systems.player.level;
|
|
}
|
|
|
|
// Check level requirement
|
|
if (item.requirements.level && playerLevel < item.requirements.level) {
|
|
return false;
|
|
}
|
|
|
|
// Add other requirement checks here
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get filtered shop items for current player
|
|
*/
|
|
getAvailableShopItems() {
|
|
return this.shopItems.filter(item => this.canPlayerUseItem(item));
|
|
}
|
|
|
|
/**
|
|
* Format item price for display
|
|
*/
|
|
formatPrice(item) {
|
|
if (!item.price) return 'Free';
|
|
|
|
const currency = item.currency || 'credits';
|
|
const price = this.game.formatNumber(item.price);
|
|
|
|
return `${price} ${currency}`;
|
|
}
|
|
|
|
/**
|
|
* Get item rarity color
|
|
*/
|
|
getRarityColor(rarity) {
|
|
const colors = {
|
|
common: '#888888',
|
|
uncommon: '#00ff00',
|
|
rare: '#0088ff',
|
|
legendary: '#ff8800',
|
|
epic: '#ff00ff'
|
|
};
|
|
|
|
return colors[rarity] || '#ffffff';
|
|
}
|
|
|
|
/**
|
|
* Refresh items from server
|
|
*/
|
|
async refresh() {
|
|
console.log('[ITEM SYSTEM] Refreshing items from server');
|
|
return this.loadFromServer();
|
|
}
|
|
|
|
/**
|
|
* Event system
|
|
*/
|
|
on(event, callback) {
|
|
if (!this.eventListeners.has(event)) {
|
|
this.eventListeners.set(event, []);
|
|
}
|
|
this.eventListeners.get(event).push(callback);
|
|
}
|
|
|
|
off(event, callback) {
|
|
if (this.eventListeners.has(event)) {
|
|
const listeners = this.eventListeners.get(event);
|
|
const index = listeners.indexOf(callback);
|
|
if (index > -1) {
|
|
listeners.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
emit(event, data) {
|
|
if (this.eventListeners.has(event)) {
|
|
for (const callback of this.eventListeners.get(event)) {
|
|
try {
|
|
callback(data);
|
|
} catch (error) {
|
|
console.error(`[ITEM SYSTEM] Error in event listener for ${event}:`, error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get system statistics
|
|
*/
|
|
getStats() {
|
|
const stats = {
|
|
totalItems: this.itemCatalog.size,
|
|
shopItems: this.shopItems.length,
|
|
lastUpdated: this.lastUpdated,
|
|
isLoading: this.isLoading,
|
|
socketConnected: !!(window.game?.socket),
|
|
socketId: window.game?.socket?.id
|
|
};
|
|
|
|
// Add category breakdown
|
|
stats.categories = {};
|
|
for (const item of this.itemCatalog.values()) {
|
|
stats.categories[item.type] = (stats.categories[item.type] || 0) + 1;
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
// Export for use in other modules
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = ItemSystem;
|
|
} else {
|
|
window.ItemSystem = ItemSystem;
|
|
}
|