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

307 lines
7.0 KiB
JavaScript

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);