const mongoose = require('mongoose'); const inventorySchema = new mongoose.Schema({ userId: { type: String, required: true, ref: 'Player' }, // Inventory settings maxSlots: { type: Number, default: 50 }, // Items array items: [{ id: { type: String, required: true }, name: { type: String, required: true }, type: { type: String, required: true, enum: ['weapon', 'armor', 'material', 'consumable'] }, rarity: { type: String, enum: ['common', 'uncommon', 'rare', 'epic', 'legendary'], default: 'common' }, quantity: { type: Number, default: 1, min: 1 }, // Item stats (for weapons/armor) stats: { attack: { type: Number, default: 0 }, defense: { type: Number, default: 0 }, speed: { type: Number, default: 0 }, criticalChance: { type: Number, default: 0 }, criticalDamage: { type: Number, default: 1.5 }, damage: { type: Number, default: 0 }, fireRate: { type: Number, default: 0 }, range: { type: Number, default: 0 }, energy: { type: Number, default: 0 }, health: { type: Number, default: 0 }, maxHealth: { type: Number, default: 0 }, durability: { type: Number, default: 0 }, weight: { type: Number, default: 0 }, energyShield: { type: Number, default: 0 } }, // Item properties description: { type: String, default: '' }, // Equipment properties equipable: { type: Boolean, default: false }, slot: { type: String, enum: ['weapon', 'armor', 'engine', 'shield', 'special'], default: null }, isEquipped: { type: Boolean, default: false }, // Consumable properties consumable: { type: Boolean, default: false }, effect: { health: { type: Number, default: 0 }, energy: { type: Number, default: 0 }, attack: { type: Number, default: 0 }, defense: { type: Number, default: 0 }, speed: { type: Number, default: 0 }, duration: { type: Number, default: 0 } }, // Stackable items stackable: { type: Boolean, default: false }, // Timestamps acquiredAt: { type: Date, default: Date.now }, lastUsed: { type: Date, default: null } }], // Equipped items equippedItems: { weapon: { type: String, default: null }, armor: { type: String, default: null }, engine: { type: String, default: null }, shield: { type: String, default: null }, special: { type: String, default: null } }, // Timestamps updatedAt: { type: Date, default: Date.now } }, { timestamps: true }); // Indexes for performance inventorySchema.index({ userId: 1 }); inventorySchema.index({ 'items.id': 1 }); inventorySchema.index({ 'items.type': 1 }); // Methods inventorySchema.methods.addItem = function(itemData) { // Check if item already exists and is stackable const existingItem = this.items.find(item => item.id === itemData.id && item.type === itemData.type && item.stackable ); if (existingItem) { existingItem.quantity += itemData.quantity || 1; } else { // Add new item const newItem = { ...itemData, quantity: itemData.quantity || 1, acquiredAt: new Date() }; this.items.push(newItem); } this.updatedAt = new Date(); return this.save(); }; inventorySchema.methods.removeItem = function(itemId, quantity = 1) { const itemIndex = this.items.findIndex(item => item.id === itemId); if (itemIndex === -1) { throw new Error('Item not found in inventory'); } const item = this.items[itemIndex]; if (item.quantity > quantity) { item.quantity -= quantity; } else { // Remove item completely this.items.splice(itemIndex, 1); // Unequip if it was equipped Object.keys(this.equippedItems).forEach(slot => { if (this.equippedItems[slot] === itemId) { this.equippedItems[slot] = null; } }); } this.updatedAt = new Date(); return this.save(); }; inventorySchema.methods.hasItem = function(itemId, quantity = 1) { const item = this.items.find(item => item.id === itemId); return item && item.quantity >= quantity; }; inventorySchema.methods.getItemCount = function(itemId) { const item = this.items.find(item => item.id === itemId); return item ? item.quantity : 0; }; inventorySchema.methods.equipItem = function(itemId, slot) { const item = this.items.find(item => item.id === itemId); if (!item) { throw new Error('Item not found in inventory'); } if (!item.equipable) { throw new Error('Item is not equipable'); } if (item.slot !== slot) { throw new Error('Item cannot be equipped in this slot'); } // Unequip current item in slot if (this.equippedItems[slot]) { const currentItem = this.items.find(item => item.id === this.equippedItems[slot]); if (currentItem) { currentItem.isEquipped = false; } } // Equip new item this.equippedItems[slot] = itemId; item.isEquipped = true; item.lastUsed = new Date(); this.updatedAt = new Date(); return this.save(); }; inventorySchema.methods.unequipItem = function(slot) { const itemId = this.equippedItems[slot]; if (!itemId) { throw new Error('No item equipped in this slot'); } const item = this.items.find(item => item.id === itemId); if (item) { item.isEquipped = false; } this.equippedItems[slot] = null; this.updatedAt = new Date(); return this.save(); }; inventorySchema.methods.useConsumable = function(itemId) { const item = this.items.find(item => item.id === itemId); if (!item) { throw new Error('Item not found in inventory'); } if (!item.consumable) { throw new Error('Item is not consumable'); } if (item.quantity <= 0) { throw new Error('No quantity left'); } // Apply effects const effects = { ...item.effect }; // Remove one from quantity item.quantity -= 1; item.lastUsed = new Date(); // Remove item if quantity is 0 if (item.quantity === 0) { const itemIndex = this.items.findIndex(item => item.id === itemId); this.items.splice(itemIndex, 1); } this.updatedAt = new Date(); this.save(); return effects; }; inventorySchema.methods.getInventorySummary = function() { const summary = { totalItems: this.items.length, usedSlots: this.items.length, maxSlots: this.maxSlots, itemsByType: {}, equippedItems: this.equippedItems }; // Count items by type this.items.forEach(item => { summary.itemsByType[item.type] = (summary.itemsByType[item.type] || 0) + item.quantity; }); return summary; }; inventorySchema.methods.getItemsByType = function(type) { return this.items.filter(item => item.type === type); }; inventorySchema.methods.getItemsByRarity = function(rarity) { return this.items.filter(item => item.rarity === rarity); }; module.exports = mongoose.model('Inventory', inventorySchema);