UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

243 lines (211 loc) 8.28 kB
'use strict'; var TinyInventory = require('./TinyInventory.cjs'); /** @typedef {import('./TinyInventory.mjs').InventoryMetadata} InventoryMetadata */ /** @typedef {import('./TinyInventory.mjs').AddItemResult} AddItemResult */ /** * @typedef {Object} TransferItemCfg - Transfer configuration. * @property {number} [slotIndex] - Sender slot index. * @property {number} [receiverSlotIndex] - Receiver slot index (optional). * @property {string} [specialSlot] - Sender special slot ID. * @property {number} [quantity=1] - Quantity to transfer. * @property {boolean} [forceSpace=false] - Whether to force addition even if space is limited. @property {boolean} [strict=false] - If true, transfer will throw if not all items can fit. */ /** * Class responsible for managing item transfers between two TinyInventory instances. */ class TinyInventoryTrader { /** @type {TinyInventory|null} */ #sender = null; /** @type {TinyInventory|null} */ #receiver = null; /////////////////////////////////////////////// /** @returns {TinyInventory|null} The currently connected sender inventory. */ get sender() { return this.#sender; } /** @returns {TinyInventory|null} The currently connected receiver inventory. */ get receiver() { return this.#receiver; } /** @param {TinyInventory} value Sets the sender inventory. */ set sender(value) { if (!(value instanceof TinyInventory) || !(this.receiver instanceof TinyInventory)) throw new Error('Both sender and receiver must be TinyInventory instances.'); this.#sender = value; } /** @param {TinyInventory} value Sets the receiver inventory. */ set receiver(value) { if (!(this.sender instanceof TinyInventory) || !(value instanceof TinyInventory)) throw new Error('Both sender and receiver must be TinyInventory instances.'); this.#receiver = value; } /////////////////////////////////////////////// /** * Manages item transfers between two TinyInventory instances. * @param {TinyInventory} [sender] - Inventory to remove items from. * @param {TinyInventory} [receiver] - Inventory to add items to. */ constructor(sender, receiver) { if (sender || receiver) { if (!(sender instanceof TinyInventory) || !(receiver instanceof TinyInventory)) throw new Error('Both sender and receiver must be TinyInventory instances.'); this.connect(sender, receiver); } } /** * Connects the sender and receiver inventories for trading. * @param {TinyInventory} sender - Inventory to remove items from. * @param {TinyInventory} receiver - Inventory to add items to. */ connect(sender, receiver) { if (!(sender instanceof TinyInventory) || !(receiver instanceof TinyInventory)) throw new Error('Both sender and receiver must be TinyInventory instances.'); this.#sender = sender; this.#receiver = receiver; } /** * Disconnects the sender and receiver inventories, preventing further transfers. */ disconnect() { this.#sender = null; this.#receiver = null; } /** * Inverts the sender and receiver roles. * Useful when performing bidirectional transfers. */ invert() { const sender = this.#sender; const receiver = this.#receiver; this.#sender = receiver; this.#receiver = sender; } /** * Transfers an item from sender to receiver. * * @param {TransferItemCfg} options - Transfer configuration. * @returns {Partial<AddItemResult>} Remaining quantity that could NOT be transferred. * @throws {Error} If sender or receiver is not connected, item does not exist in sender, * or if strict mode is enabled and receiver has no space for full transfer. */ transferItem({ slotIndex, specialSlot, quantity = 1, receiverSlotIndex, forceSpace = false, strict = false, }) { if (!this.#sender || !this.#receiver) throw new Error('Sender and receiver inventories must be connected.'); // Get the item from sender let item; if (specialSlot) { item = this.#sender.getSpecialItem(specialSlot); if (!item) throw new Error(`No item found in sender special slot '${specialSlot}'.`); } else if (typeof slotIndex === 'number') { item = this.#sender.getItemFrom(slotIndex); if (!item) throw new Error(`No item found in sender slot ${slotIndex}.`); } else throw new Error('Must provide either slotIndex or specialSlot.'); // Match quantity if (item.quantity < quantity) throw new Error(`Sender does not have enough quantity of '${item.id}' to transfer.`); // Clone item for transfer const transferItem = { id: item.id, quantity, metadata: item.metadata, }; let result; // ---------- STRICT MODE PRE-CHECK ---------- if (strict) { if (typeof receiverSlotIndex === 'number') { const existing = this.#receiver.existsItemAt(receiverSlotIndex) ? this.#receiver.getItemFrom(receiverSlotIndex) : null; if (existing) { if (existing.id !== transferItem.id) throw new Error(`Receiver slot ${receiverSlotIndex} contains a different item.`); const def = TinyInventory.getItem(existing.id); const spaceLeft = def.maxStack - existing.quantity; if (transferItem.quantity > spaceLeft) throw new Error(`Strict mode: not enough space in receiver slot ${receiverSlotIndex}.`); } } else { // Simulate addItem result const simulated = this.#receiver.clone().addItem({ itemId: transferItem.id, quantity: transferItem.quantity, metadata: transferItem.metadata, forceSpace, }); if (simulated.remaining > 0) throw new Error(`Strict mode: not enough space in receiver inventory.`); } } // ---------- END STRICT CHECK ---------- // If receiverSlotIndex is informed → use setItem on receiver if (typeof receiverSlotIndex === 'number') { const existing = this.#receiver.existsItemAt(receiverSlotIndex) ? this.#receiver.getItemFrom(receiverSlotIndex) : null; let movedQty = transferItem.quantity; // If there is already item in this slot if (existing) { if (existing.id !== transferItem.id) throw new Error(`Receiver slot ${receiverSlotIndex} contains a different item.`); const def = TinyInventory.getItem(existing.id); const spaceLeft = def.maxStack - existing.quantity; movedQty = Math.min(spaceLeft, transferItem.quantity); if (movedQty > 0) { this.#receiver.setItem({ slotIndex: receiverSlotIndex, item: { ...existing, quantity: existing.quantity + movedQty }, forceSpace, }); } } else { // Empty Slot → just seal the whole item this.#receiver.setItem({ slotIndex: receiverSlotIndex, item: transferItem, forceSpace, }); } // Now we calculate what's left result = { remaining: transferItem.quantity - movedQty }; } else { result = this.#receiver.addItem({ itemId: transferItem.id, quantity: transferItem.quantity, metadata: transferItem.metadata, forceSpace, }); } // Remove successfully transferred quantity from sender const removedQty = transferItem.quantity - result.remaining; if (removedQty > 0) { if (specialSlot) { this.#sender.unequipItem({ slotId: specialSlot, quantity: removedQty, forceSpace }); } else if (typeof slotIndex === 'number') { const remainingQty = item.quantity - removedQty; this.#sender.setItem({ slotIndex, item: remainingQty > 0 ? { ...item, quantity: remainingQty } : null, forceSpace, }); } } return result; } /** * Transfers multiple items at once. * @param {TransferItemCfg[]} items - Array of transfer configs (see transferItem options). * @returns {Partial<AddItemResult>[]} Array of remaining quantities for each item. */ transferMultiple(items) { return items.map((cfg) => this.transferItem(cfg)); } } module.exports = TinyInventoryTrader;