1135 lines
41 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
}
|