307 lines
7.0 KiB
JavaScript
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);
|