API/Client/js/core/Inventory.js
2026-01-24 16:47:19 -04:00

1135 lines
41 KiB
JavaScript

/**
* Galaxy Strike Online - Inventory System
* Manages player items, equipment, and storage
*/
class Inventory {
constructor(gameEngine) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.constructor', {
gameEngineProvided: !!gameEngine
});
this.game = gameEngine;
// Inventory configuration
this.maxSlots = 30;
this.baseMaxSlots = 30; // Base slots without starbase bonuses
this.maxStack = 999;
// Starbase inventory bonuses
this.starbaseBonusSlots = 0;
this.totalMaxSlots = this.baseMaxSlots + this.starbaseBonusSlots;
// Inventory data - ensure items is always an array
this.items = [];
this.equipment = {
weapon: null,
armor: null,
engine: null,
shield: null,
accessory: null
};
// Item categories
this.categories = {
weapon: 'Weapons',
armor: 'Armor',
engine: 'Engines',
shield: 'Shields',
accessory: 'Accessories',
consumable: 'Consumables',
material: 'Materials',
cosmetic: 'Cosmetics'
};
// Item rarities
this.rarities = {
common: { name: 'Common', color: '#888888', multiplier: 1 },
uncommon: { name: 'Uncommon', color: '#00ff00', multiplier: 1.2 },
rare: { name: 'Rare', color: '#0088ff', multiplier: 1.5 },
epic: { name: 'Epic', color: '#8833ff', multiplier: 2 },
legendary: { name: 'Legendary', color: '#ff8800', multiplier: 3 }
};
if (debugLogger) debugLogger.endStep('Inventory.constructor', {
maxSlots: this.maxSlots,
baseMaxSlots: this.baseMaxSlots,
maxStack: this.maxStack,
starbaseBonusSlots: this.starbaseBonusSlots,
totalMaxSlots: this.totalMaxSlots,
initialItemCount: this.items.length,
equipmentSlots: Object.keys(this.equipment).length,
categoriesCount: Object.keys(this.categories).length,
raritiesCount: Object.keys(this.rarities).length
});
}
async initialize() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.initialize', {
currentItemCount: this.items.length
});
// Initialize with starting items
if (this.items.length === 0) {
if (debugLogger) debugLogger.logStep('Adding starting items to empty inventory');
this.addStartingItems();
} else {
if (debugLogger) debugLogger.logStep('Inventory already has items, skipping starting items');
}
if (debugLogger) debugLogger.endStep('Inventory.initialize', {
finalItemCount: this.items.length,
startingItemsAdded: this.items.length > 0
});
}
addStartingItems() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.addStartingItems', {
currentItemCount: this.items.length,
maxSlots: this.maxSlots
});
const startingItems = [
{
id: 'starter_blaster_common',
name: 'Common Blaster',
type: 'weapon',
rarity: 'common',
quantity: 1,
stats: { attack: 5, criticalChance: 0.02 },
description: 'A reliable basic blaster for new pilots',
equipable: true,
slot: 'weapon'
},
{
id: 'basic_armor_common',
name: 'Basic Armor',
type: 'armor',
rarity: 'common',
quantity: 1,
stats: { defense: 3 },
description: 'Light armor providing basic protection',
equipable: true,
slot: 'armor'
}
];
if (debugLogger) debugLogger.logStep('Adding starting items', {
startingItemCount: startingItems.length,
startingItems: startingItems.map(item => ({
id: item.id,
name: item.name,
type: item.type,
quantity: item.quantity
}))
});
startingItems.forEach(item => {
console.log(`[DEBUG] Adding starting item: ${item.name}`);
const result = this.addItem(item);
console.log(`[DEBUG] Starting item add result: ${result}, inventory size: ${this.items.length}`);
});
// Equip starter items
console.log('[INVENTORY] Equipping starter items');
if (debugLogger) debugLogger.logStep('Equipping starter items');
// Equip starter blaster
const blasterItem = this.items.find(item => item.id === 'starter_blaster');
if (blasterItem) {
console.log('[INVENTORY] Equipping starter blaster');
this.equipItem(blasterItem.id);
}
// Equip basic armor
const armorItem = this.items.find(item => item.id === 'basic_armor');
if (armorItem) {
console.log('[INVENTORY] Equipping basic armor');
this.equipItem(armorItem.id);
}
// Auto-stack starting items
if (debugLogger) debugLogger.logStep('Auto-stacking starting items');
this.autoStackItems();
if (debugLogger) debugLogger.endStep('Inventory.addStartingItems', {
finalItemCount: this.items.length,
itemsAdded: startingItems.length
});
}
// Item management
addItem(itemData, quantity = 1) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.addItem', {
itemData: itemData,
quantity: quantity,
currentItemCount: this.items.length,
maxSlots: this.maxSlots,
maxStack: this.maxStack
});
if (!itemData || !itemData.id) {
console.error('Invalid item data:', itemData);
if (debugLogger) debugLogger.errorEvent('Inventory.addItem', new Error('Invalid item data'), {
itemData: itemData,
quantity: quantity
});
return false;
}
const item = { ...itemData, quantity };
// Auto-stack: Check if exact same item already exists (same type, rarity, and stats)
const existingItem = this.items.find(i =>
i.id === item.id &&
i.type === item.type &&
i.rarity === item.rarity &&
JSON.stringify(i.stats) === JSON.stringify(item.stats)
);
if (existingItem) {
if (debugLogger) debugLogger.logStep('Found existing item for stacking', {
existingItemId: existingItem.id,
existingItemName: existingItem.name,
currentQuantity: existingItem.quantity,
addingQuantity: quantity,
maxStack: this.maxStack
});
// Stack items up to max stack
const totalQuantity = existingItem.quantity + quantity;
if (totalQuantity <= this.maxStack) {
existingItem.quantity = totalQuantity;
this.game.showNotification(`${item.name} +${quantity} (Stacked)`, 'success', 2000);
if (debugLogger) debugLogger.logStep('Item stacked successfully', {
itemId: existingItem.id,
oldQuantity: existingItem.quantity - quantity,
newQuantity: existingItem.quantity,
added: quantity
});
} else {
// Add to existing stack and create new item if needed
const remainingQuantity = totalQuantity - this.maxStack;
existingItem.quantity = this.maxStack;
if (debugLogger) debugLogger.logStep('Stack overflow, creating new item', {
itemId: existingItem.id,
maxStackReached: this.maxStack,
remainingQuantity: remainingQuantity,
currentSlots: this.items.length,
maxSlots: this.totalMaxSlots
});
// Try to add remaining quantity as new item
if (this.items.length < this.totalMaxSlots) {
const newItem = { ...itemData, quantity: remainingQuantity };
this.items.push(newItem);
this.game.showNotification(`${item.name} +${quantity} (Stack: ${this.maxStack}, New: ${remainingQuantity})`, 'success', 2000);
if (debugLogger) debugLogger.logStep('New item created for overflow', {
newItemId: newItem.id,
newItemQuantity: remainingQuantity,
totalSlotsUsed: this.items.length
});
} else {
this.game.showNotification(`${item.name} +${quantity} (Stack full: ${this.maxStack})`, 'warning', 3000);
if (debugLogger) debugLogger.logStep('Inventory full, overflow lost', {
lostQuantity: remainingQuantity,
maxSlots: this.totalMaxSlots
});
}
}
} else {
// Find empty slot
if (this.items.length >= this.totalMaxSlots) {
this.game.showNotification('Inventory is full!', 'error', 3000);
if (debugLogger) debugLogger.endStep('Inventory.addItem', {
success: false,
reason: 'Inventory full',
itemId: item.id,
itemName: item.name,
quantity: quantity,
currentSlots: this.items.length,
maxSlots: this.totalMaxSlots
});
return false;
}
this.items.push(item);
this.game.showNotification(`Acquired ${item.name}`, 'success', 2000);
if (debugLogger) debugLogger.logStep('New item added to inventory', {
itemId: item.id,
itemName: item.name,
itemType: item.type,
itemRarity: item.rarity,
quantity: quantity,
slotUsed: this.items.length - 1
});
}
this.autoStackItems(); // Reorganize inventory
if (debugLogger) debugLogger.endStep('Inventory.addItem', {
success: true,
itemId: item.id,
itemName: item.name,
quantity: quantity,
finalItemCount: this.items.length,
slotsUsed: this.items.length,
maxSlots: this.maxSlots
});
return true;
}
// Auto-stack and organize inventory
autoStackItems() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.autoStackItems', {
currentItemCount: this.items.length,
maxSlots: this.maxSlots,
maxStack: this.maxStack
});
const stackedItems = {};
const originalItemCount = this.items.length;
// Group items by exact ID, type, rarity, and stats
this.items.forEach(item => {
const stackKey = `${item.id}_${item.type}_${item.rarity}_${JSON.stringify(item.stats || {})}`;
if (!stackedItems[stackKey]) {
stackedItems[stackKey] = {
...item,
quantity: 0
};
}
stackedItems[stackKey].quantity += item.quantity;
});
if (debugLogger) debugLogger.logStep('Items grouped for stacking', {
originalItemCount: originalItemCount,
stackGroupsCreated: Object.keys(stackedItems).length,
stackGroups: Object.entries(stackedItems).map(([key, item]) => ({
stackKey: key,
itemId: item.id,
itemName: item.name,
totalQuantity: item.quantity
}))
});
// Convert back to array and limit by max stack
const newItems = [];
const stackedValues = Object.values(stackedItems);
for (const item of stackedValues) {
if (newItems.length >= this.totalMaxSlots) break;
while (item.quantity > 0 && newItems.length < this.totalMaxSlots) {
const stackQuantity = Math.min(item.quantity, this.maxStack);
newItems.push({
...item,
quantity: stackQuantity
});
item.quantity -= stackQuantity;
}
}
this.items = newItems;
if (debugLogger) debugLogger.endStep('Inventory.autoStackItems', {
originalItemCount: originalItemCount,
newItemCount: this.items.length,
stackGroupsProcessed: stackedValues.length,
slotsUsed: this.items.length,
maxSlots: this.maxSlots
});
}
removeItem(itemId, quantity = 1) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.removeItem', {
itemId: itemId,
quantity: quantity,
currentItemCount: this.items.length
});
const itemIndex = this.items.findIndex(item => item.id === itemId);
if (itemIndex === -1) {
if (debugLogger) debugLogger.endStep('Inventory.removeItem', {
success: false,
reason: 'Item not found',
itemId: itemId,
quantity: quantity
});
return false;
}
const item = this.items[itemIndex];
const oldQuantity = item.quantity;
if (item.quantity > quantity) {
item.quantity -= quantity;
if (debugLogger) debugLogger.logStep('Item quantity reduced', {
itemId: itemId,
itemName: item.name,
oldQuantity: oldQuantity,
newQuantity: item.quantity,
removed: quantity
});
} else {
this.items.splice(itemIndex, 1);
if (debugLogger) debugLogger.logStep('Item completely removed', {
itemId: itemId,
itemName: item.name,
oldQuantity: oldQuantity,
removed: oldQuantity,
itemIndex: itemIndex
});
}
if (debugLogger) debugLogger.endStep('Inventory.removeItem', {
success: true,
itemId: itemId,
itemName: item.name,
quantityRemoved: quantity,
finalItemCount: this.items.length,
itemStillExists: this.items.find(i => i.id === itemId) !== undefined
});
return true;
}
hasItem(itemId, quantity = 1) {
const item = this.items.find(item => item.id === itemId);
return item && item.quantity >= quantity;
}
getItemCount(itemId) {
const item = this.items.find(item => item.id === itemId);
return item ? item.quantity : 0;
}
// Equipment management
equipItem(itemId) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.equipItem', {
itemId: itemId,
currentItemCount: this.items.length,
currentEquipment: this.equipment
});
console.log('Attempting to equip item:', itemId);
const item = this.items.find(item => item.id === itemId);
console.log('Found item:', item);
if (!item) {
console.log('Item not found in inventory');
this.game.showNotification('Item not found', 'error', 3000);
if (debugLogger) debugLogger.endStep('Inventory.equipItem', {
success: false,
reason: 'Item not found',
itemId: itemId
});
return false;
}
if (!item.equipable) {
console.log('Item is not equipable:', item);
this.game.showNotification('This item cannot be equipped', 'error', 3000);
if (debugLogger) debugLogger.endStep('Inventory.equipItem', {
success: false,
reason: 'Item not equipable',
itemId: itemId,
itemName: item.name,
itemType: item.type
});
return false;
}
const slot = item.type;
console.log('Equipping to slot:', slot);
const currentItem = this.equipment[slot];
if (debugLogger) debugLogger.logStep('Equipment slot validation', {
itemId: itemId,
itemName: item.name,
itemType: item.type,
targetSlot: slot,
currentItemInSlot: currentItem ? currentItem.name : null,
itemStats: item.stats
});
// Unequip current item if exists
if (currentItem) {
console.log('Unequipping current item:', currentItem);
if (debugLogger) debugLogger.logStep('Unequipping current item', {
currentItemName: currentItem.name,
currentItemStats: currentItem.stats,
returningToInventory: true
});
this.addItem(currentItem, 1);
}
// Equip new item
this.equipment[slot] = item;
this.removeItem(itemId, 1);
// Apply item stats to player
this.applyEquipmentStats();
// Update UI
this.updateUI();
console.log('Successfully equipped:', item.name);
this.game.showNotification(`Equipped ${item.name}`, 'success', 3000);
if (debugLogger) debugLogger.endStep('Inventory.equipItem', {
success: true,
itemId: itemId,
itemName: item.name,
slot: slot,
previousItem: currentItem ? currentItem.name : null,
newEquipmentState: this.equipment,
finalItemCount: this.items.length
});
return true;
}
unequipItem(slot) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.unequipItem', {
slot: slot,
currentEquipment: this.equipment,
currentItemCount: this.items.length,
maxSlots: this.maxSlots
});
console.log('Attempting to unequip slot:', slot);
const item = this.equipment[slot];
if (!item) {
console.log('No item in slot:', slot);
if (debugLogger) debugLogger.endStep('Inventory.unequipItem', {
success: false,
reason: 'No item in slot',
slot: slot
});
return false;
}
// Check inventory space
if (this.items.length >= this.totalMaxSlots) {
console.log('Inventory is full, cannot unequip');
this.game.showNotification('Inventory is full!', 'error', 3000);
if (debugLogger) debugLogger.endStep('Inventory.unequipItem', {
success: false,
reason: 'Inventory full',
slot: slot,
itemName: item.name,
currentItemCount: this.items.length,
maxSlots: this.maxSlots
});
return false;
}
if (debugLogger) debugLogger.logStep('Unequipping item', {
slot: slot,
itemName: item.name,
itemStats: item.stats,
returningToInventory: true,
inventorySpaceAvailable: this.items.length < this.maxSlots
});
// Unequip item
this.equipment[slot] = null;
this.addItem(item, 1);
// Remove item stats from player
this.applyEquipmentStats();
// Update UI
this.updateUI();
console.log('Successfully unequipped:', item.name);
this.game.showNotification(`Unequipped ${item.name}`, 'info', 3000);
if (debugLogger) debugLogger.endStep('Inventory.unequipItem', {
success: true,
slot: slot,
itemName: item.name,
newEquipmentState: this.equipment,
finalItemCount: this.items.length
});
return true;
}
applyEquipmentStats() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.applyEquipmentStats', {
currentEquipment: this.equipment,
equippedItemsCount: Object.values(this.equipment).filter(item => item !== null).length
});
// Reset player stats to base
const player = this.game.systems.player;
const oldPlayerAttributes = { ...player.attributes };
if (debugLogger) debugLogger.logStep('Applying equipment bonuses', {
oldPlayerAttributes: oldPlayerAttributes,
equipmentToApply: Object.entries(this.equipment).filter(([slot, item]) => item !== null)
});
// Apply equipment bonuses
for (const [slot, item] of Object.entries(this.equipment)) {
if (item && item.stats) {
for (const [stat, value] of Object.entries(item.stats)) {
if (player.attributes[stat] !== undefined) {
const oldValue = player.attributes[stat];
player.attributes[stat] += value;
if (debugLogger) debugLogger.logStep('Stat bonus applied', {
slot: slot,
itemName: item.name,
stat: stat,
value: value,
oldValue: oldValue,
newValue: player.attributes[stat]
});
}
}
}
}
player.updateUI();
if (debugLogger) debugLogger.endStep('Inventory.applyEquipmentStats', {
oldPlayerAttributes: oldPlayerAttributes,
newPlayerAttributes: player.attributes,
statChanges: Object.entries(player.attributes).map(([stat, newValue]) => ({
stat: stat,
oldValue: oldPlayerAttributes[stat],
newValue: newValue,
change: newValue - oldPlayerAttributes[stat]
})).filter(change => change.change !== 0)
});
}
// Consumable usage
useItem(itemId) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.useItem', {
itemId: itemId,
currentItemCount: this.items.length
});
const item = this.items.find(item => item.id === itemId);
if (!item || !item.consumable) {
this.game.showNotification('This item cannot be used', 'error', 3000);
if (debugLogger) debugLogger.endStep('Inventory.useItem', {
success: false,
reason: !item ? 'Item not found' : 'Item not consumable',
itemId: itemId
});
return false;
}
if (debugLogger) debugLogger.logStep('Using consumable item', {
itemId: itemId,
itemName: item.name,
itemEffect: item.effect,
oldQuantity: item.quantity
});
const player = this.game.systems.player;
const oldPlayerHealth = player.attributes.health;
const oldPlayerEnergy = player.attributes.energy;
// Apply item effects
if (item.effect) {
if (item.effect.heal) {
player.heal(item.effect.heal);
this.game.showNotification(`Restored ${item.effect.heal} health`, 'success', 2000);
if (debugLogger) debugLogger.logStep('Healing effect applied', {
healAmount: item.effect.heal,
oldHealth: oldPlayerHealth,
newHealth: player.attributes.health
});
}
if (item.effect.energy) {
player.restoreEnergy(item.effect.energy);
this.game.showNotification(`Restored ${item.effect.energy} energy`, 'success', 2000);
if (debugLogger) debugLogger.logStep('Energy effect applied', {
energyAmount: item.effect.energy,
oldEnergy: oldPlayerEnergy,
newEnergy: player.attributes.energy
});
}
if (item.effect.buff) {
// Apply temporary buffs (would need buff system)
this.game.showNotification(`Buff applied: ${item.effect.buff}`, 'success', 2000);
if (debugLogger) debugLogger.logStep('Buff effect applied', {
buffType: item.effect.buff
});
}
}
// Remove consumed item
this.removeItem(itemId, 1);
if (debugLogger) debugLogger.endStep('Inventory.useItem', {
success: true,
itemId: itemId,
itemName: item.name,
effectsApplied: item.effect,
finalItemCount: this.items.length
});
return true;
}
// Item generation
generateItem(type, rarity = 'common', level = 1) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.generateItem', {
type: type,
rarity: rarity,
level: level
});
const itemTemplates = {
weapon: {
common: [
{ name: 'Basic Blaster', stats: { attack: 5 } },
{ name: 'Laser Rifle', stats: { attack: 6 } }
],
uncommon: [
{ name: 'Plasma Cannon', stats: { attack: 8, criticalChance: 0.02 } },
{ name: 'Ion Blaster', stats: { attack: 7, speed: 2 } }
],
rare: [
{ name: 'Quantum Rifle', stats: { attack: 12, criticalChance: 0.05 } },
{ name: 'Fusion Cannon', stats: { attack: 15, criticalDamage: 0.2 } }
]
},
armor: {
common: [
{ name: 'Basic Plating', stats: { defense: 3, maxHealth: 10 } },
{ name: 'Light Armor', stats: { defense: 4, speed: -1 } }
],
uncommon: [
{ name: 'Reinforced Plating', stats: { defense: 6, maxHealth: 20 } },
{ name: 'Energy Shield', stats: { defense: 5, maxEnergy: 10 } }
],
rare: [
{ name: 'Titanium Armor', stats: { defense: 10, maxHealth: 40 } },
{ name: 'Phase Shield', stats: { defense: 8, criticalChance: -0.05 } }
]
}
};
const templates = itemTemplates[type]?.[rarity] || itemTemplates.weapon.common;
const template = templates[Math.floor(Math.random() * templates.length)];
const rarityData = this.rarities[rarity];
const levelMultiplier = 1 + (level - 1) * 0.1;
const generatedItem = {
id: `${type}_${rarity}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
name: template.name,
type: type,
rarity: rarity,
quantity: 1,
stats: Object.fromEntries(
Object.entries(template.stats).map(([stat, value]) => [
stat,
Math.floor(value * rarityData.multiplier * levelMultiplier)
])
),
description: `Level ${level} ${rarityData.name} ${type}`,
equipable: true,
value: Math.floor(50 * rarityData.multiplier * levelMultiplier)
};
if (debugLogger) debugLogger.endStep('Inventory.generateItem', {
type: type,
rarity: rarity,
level: level,
templateUsed: template.name,
rarityMultiplier: rarityData.multiplier,
levelMultiplier: levelMultiplier,
generatedItem: generatedItem,
finalStats: generatedItem.stats,
itemValue: generatedItem.value
});
return generatedItem;
}
// UI updates
clear() {
const debugLogger = window.debugLogger;
const oldState = {
itemCount: this.items.length,
maxSlots: this.maxSlots,
cargo: this.cargo ? this.cargo.length : 0,
maxCargo: this.maxCargo
};
if (debugLogger) debugLogger.startStep('Inventory.clear', {
oldState: oldState
});
this.items = [];
this.maxSlots = 30;
this.cargo = [];
this.maxCargo = 100;
this.updateUI();
if (debugLogger) debugLogger.endStep('Inventory.clear', {
oldState: oldState,
newState: {
itemCount: this.items.length,
maxSlots: this.maxSlots,
cargo: this.cargo.length,
maxCargo: this.maxCargo
}
});
}
reset() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.reset');
this.clear();
if (debugLogger) debugLogger.endStep('Inventory.reset', {
cleared: true
});
}
updateUI() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('Inventory.updateUI', {
itemCount: this.items.length,
equipmentCount: Object.values(this.equipment).filter(item => item !== null).length
});
this.updateInventoryGrid();
this.updateEquipmentDisplay();
if (debugLogger) debugLogger.endStep('Inventory.updateUI', {
inventoryGridUpdated: true,
equipmentDisplayUpdated: true
});
}
updateInventoryGrid() {
const grid = document.getElementById('inventoryGrid');
if (!grid) return;
grid.innerHTML = '';
// Create inventory slots using total max slots (base + starbase bonuses)
for (let i = 0; i < this.totalMaxSlots; i++) {
const slot = document.createElement('div');
slot.className = 'inventory-slot';
// Add special styling for starbase bonus slots
if (i >= this.baseMaxSlots) {
slot.classList.add('starbase-bonus-slot');
}
const item = this.items[i];
if (item) {
const iconHtml = this.getItemIconHtml(item.type, '24px');
const quantityBadge = item.quantity > 1 ? `<div class="item-quantity">${item.quantity}</div>` : '';
slot.innerHTML = `
<div class="item-card ${item.rarity}" data-item-id="${item.id}">
<div class="item-icon">
${iconHtml}
</div>
<div class="item-info">
<div class="item-name">${item.name}</div>
<div class="item-rarity" style="color: ${this.rarities[item.rarity].color}">
${this.rarities[item.rarity].name}
</div>
</div>
${quantityBadge}
</div>
`;
// Add click handlers
const itemCard = slot.querySelector('.item-card');
itemCard.addEventListener('click', () => this.showItemDetails(item));
if (item.equipable) {
itemCard.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.equipItem(item.id);
});
}
if (item.consumable) {
itemCard.addEventListener('dblclick', () => this.useItem(item.id));
}
} else {
slot.innerHTML = '<div class="empty-slot"></div>';
}
grid.appendChild(slot);
}
}
updateEquipmentDisplay() {
// Update equipment slots in UI
for (const [slot, item] of Object.entries(this.equipment)) {
const slotElement = document.getElementById(`equip-${slot}`);
if (slotElement) {
if (item) {
const iconHtml = this.getItemIconHtml(item.type, '24px');
slotElement.innerHTML = `
<div class="equipped-item ${item.rarity}">
${iconHtml}
<div class="item-name">${item.name}</div>
</div>
`;
// Add click handler to unequip
slotElement.onclick = () => this.unequipItem(slot);
} else {
slotElement.innerHTML = `<div class="empty-equip-slot">Empty</div>`;
slotElement.onclick = null;
}
}
}
}
showItemDetails(item) {
const detailsElement = document.getElementById('itemDetails');
if (!detailsElement) return;
const rarityData = this.rarities[item.rarity];
const statsHtml = item.stats ?
Object.entries(item.stats)
.map(([stat, value]) => `<div class="item-stat">${stat}: +${value}</div>`)
.join('') : '';
detailsElement.innerHTML = `
<h3 style="color: ${rarityData.color}">${item.name}</h3>
<p>${item.description}</p>
<div class="item-type">Type: ${this.categories[item.type]}</div>
<div class="item-rarity">Rarity: ${rarityData.name}</div>
${item.quantity > 1 ? `<div class="item-quantity">Quantity: ${item.quantity}</div>` : ''}
${statsHtml ? `<div class="item-stats">${statsHtml}</div>` : ''}
<div class="item-actions">
${item.equipable ? `<button class="btn btn-primary" onclick="if(window.game && window.game.systems) window.game.systems.inventory.equipItem('${item.id}')">Equip</button>` : ''}
${item.consumable ? `<button class="btn btn-success" onclick="if(window.game && window.game.systems) window.game.systems.inventory.useItem('${item.id}')">Use</button>` : ''}
</div>
`;
}
getItemIcon(type) {
const icons = {
weapon: 'fa-sword',
armor: 'fa-shield-alt',
engine: 'fa-rocket',
shield: 'fa-shield-virus',
accessory: 'fa-ring',
consumable: 'fa-flask',
material: 'fa-cube',
cosmetic: 'fa-star'
};
const iconClass = icons[type] || 'fa-cube';
// Use texture manager for icon fallback if available
if (this.game.systems.textureManager) {
return this.game.systems.textureManager.getIcon(iconClass);
}
return iconClass;
}
getItemIconHtml(type, size = '32px') {
// Use texture manager for icon HTML if available
if (this.game.systems.textureManager && typeof this.game.systems.textureManager.getItemIconElement === 'function') {
return this.game.systems.textureManager.getItemIconElement(this.getItemIcon(type), size);
}
// Fallback to FontAwesome
const iconClass = this.getItemIcon(type);
return `<i class="fas ${iconClass}" style="font-size: ${size};"></i>`;
}
// Starbase inventory integration methods
updateStarbaseBonusSlots(bonusSlots) {
this.starbaseBonusSlots = bonusSlots;
this.totalMaxSlots = this.baseMaxSlots + this.starbaseBonusSlots;
}
getInventoryInfo() {
return {
currentSlots: this.items.length,
baseMaxSlots: this.baseMaxSlots,
starbaseBonusSlots: this.starbaseBonusSlots,
totalMaxSlots: this.totalMaxSlots,
availableSlots: this.totalMaxSlots - this.items.length
};
}
// Save/Load
save() {
const debugLogger = window.debugLogger;
// if (debugLogger) debugLogger.startStep('Inventory.save', {
// itemCount: this.items.length,
// equipmentCount: Object.values(this.equipment).filter(item => item !== null).length,
// baseMaxSlots: this.baseMaxSlots,
// starbaseBonusSlots: this.starbaseBonusSlots
// });
const saveData = {
items: this.items,
equipment: this.equipment,
baseMaxSlots: this.baseMaxSlots,
starbaseBonusSlots: this.starbaseBonusSlots
};
// if (debugLogger) debugLogger.endStep('Inventory.save', {
// saveDataSize: JSON.stringify(saveData).length,
// itemsSaved: this.items.length,
// equipmentSaved: Object.keys(this.equipment).length,
// saveData: saveData
// });
return saveData;
}
load(data) {
const debugLogger = window.debugLogger;
const oldState = {
itemCount: this.items.length,
equipmentCount: Object.values(this.equipment).filter(item => item !== null).length,
baseMaxSlots: this.baseMaxSlots,
starbaseBonusSlots: this.starbaseBonusSlots
};
if (debugLogger) debugLogger.startStep('Inventory.load', {
oldState: oldState,
loadData: data
});
try {
if (data.items) {
// Ensure items is always an array
this.items = Array.isArray(data.items) ? data.items : [];
if (debugLogger) debugLogger.logStep('Loading items', {
itemsLoaded: this.items.length,
itemsArray: Array.isArray(data.items),
autoStacking: true
});
this.autoStackItems();
}
if (data.equipment) {
this.equipment = data.equipment;
if (debugLogger) debugLogger.logStep('Loading equipment', {
equipmentLoaded: Object.keys(data.equipment).length,
equipmentState: data.equipment
});
}
// Load starbase bonus slots
if (data.baseMaxSlots !== undefined) {
this.baseMaxSlots = data.baseMaxSlots;
if (debugLogger) debugLogger.logStep('Loading base max slots', {
oldBaseMaxSlots: oldState.baseMaxSlots,
newBaseMaxSlots: this.baseMaxSlots
});
}
if (data.starbaseBonusSlots !== undefined) {
this.starbaseBonusSlots = data.starbaseBonusSlots;
if (debugLogger) debugLogger.logStep('Loading starbase bonus slots', {
oldStarbaseBonusSlots: oldState.starbaseBonusSlots,
newStarbaseBonusSlots: this.starbaseBonusSlots
});
}
// Recalculate total max slots
this.totalMaxSlots = this.baseMaxSlots + this.starbaseBonusSlots;
if (debugLogger) debugLogger.logStep('Recalculating total max slots', {
baseMaxSlots: this.baseMaxSlots,
starbaseBonusSlots: this.starbaseBonusSlots,
totalMaxSlots: this.totalMaxSlots
});
// Update UI to show loaded equipment
this.updateUI();
if (debugLogger) debugLogger.endStep('Inventory.load', {
success: true,
oldState: oldState,
newState: {
itemCount: this.items.length,
equipmentCount: Object.values(this.equipment).filter(item => item !== null).length,
baseMaxSlots: this.baseMaxSlots,
starbaseBonusSlots: this.starbaseBonusSlots,
totalMaxSlots: this.totalMaxSlots
}
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('Inventory.load', error, {
oldState: oldState,
loadData: data,
error: error.message
});
throw error;
}
}
}