#include "Inventory.h" #include "ItemFilter.h" #include "Constants.h" #include "Area.h" using namespace Framework; InventoryInteraction::InventoryInteraction(Inventory* current, Inventory* other, Direction dir) : current(current), other(other), dir(dir) { lock(); } InventoryInteraction::InventoryInteraction(const InventoryInteraction& interaction) : InventoryInteraction(interaction.current, interaction.other, interaction.dir) {} InventoryInteraction::~InventoryInteraction() { unlock(); } void InventoryInteraction::lock() { if (!current || !other) return; if (current->location.x < other->location.x) { current->cs.lock(); other->cs.lock(); return; } else if (current->location.x == other->location.x) { if (current->location.y < other->location.y) { current->cs.lock(); other->cs.lock(); return; } else if (current->location.y == other->location.y) { if (current->location.z < other->location.z) { current->cs.lock(); other->cs.lock(); return; } } } other->cs.lock(); current->cs.lock(); } void InventoryInteraction::unlock() { if (!current || !other) return; if (current->location.x < other->location.x) { current->cs.unlock(); other->cs.unlock(); return; } else if (current->location.x == other->location.x) { if (current->location.y < other->location.y) { current->cs.unlock(); other->cs.unlock(); return; } else if (current->location.y == other->location.y) { if (current->location.z < other->location.z) { current->cs.unlock(); other->cs.unlock(); return; } } } other->cs.unlock(); current->cs.unlock(); } void InventoryInteraction::transaction(Inventory* zSource, Inventory* zTarget, ItemFilter* zFilter, Direction sourceView, Direction targetView, int count) { // TODO: rewrite this such that for each source slot all target slots are looped trough auto sourceSlot = zSource->pullSlotsOrder->begin(); for (auto targetSlot = zTarget->pushSlotsOrder->begin(); targetSlot; ) { int amount = count; if (!targetSlot->isFull()) { if (targetSlot->zStack()) { Array* zSurceListe = zSource->itemCache->safeGet(targetSlot->zStack()->zItem()->zItemType()->getId(), 0); if (zSurceListe) { Array toDelete; int index = 0; for (auto sourceSlot = zSurceListe->begin(); sourceSlot; sourceSlot++, index++) { if (zFilter && !zFilter->matchItem(sourceSlot->zStack()->zItem())) continue; int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), sourceView), count); int tmp = number; if (number > 0 && zSource->allowPullStack(sourceSlot, sourceView) && zTarget->allowPushStack(targetSlot, targetView, sourceSlot->zStack()->zItem(), tmp)) { number = MIN(number, tmp); ItemStack* stack = sourceSlot->takeItemsOut(number, dir); if (stack) { if (!sourceSlot->zStack()) toDelete.add(index, 0); targetSlot->addItems(stack, dir); zSource->updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId()); zSource->afterPullStack(sourceSlot, sourceView, targetSlot->zStack()->zItem(), number); zTarget->afterPushStack(targetSlot, targetView, targetSlot->zStack()->zItem(), number); if (stack->getSize()) throw stack; stack->release(); count -= number; if (count == 0) break; } } } for (auto indexToDelete = toDelete.begin(); indexToDelete; indexToDelete++) zSurceListe->remove(indexToDelete); if (count == 0) return; } } else { while (sourceSlot && (!sourceSlot->zStack() || (zFilter && !zFilter->matchItem(sourceSlot->zStack()->zItem())))) sourceSlot++; if (!sourceSlot) return; int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), sourceView), count); int tmp = number; if (number > 0 && zSource->allowPullStack(sourceSlot, sourceView) && zTarget->allowPushStack(targetSlot, targetView, sourceSlot->zStack()->zItem(), tmp)) { number = MIN(number, tmp); if (number > 0) { ItemStack* stack = sourceSlot->takeItemsOut(number, dir); if (stack) { targetSlot->addItems(stack, dir); zSource->updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId()); zTarget->updateCache(targetSlot, -1); zSource->afterPullStack(sourceSlot, sourceView, targetSlot->zStack()->zItem(), number); zTarget->afterPushStack(targetSlot, targetView, targetSlot->zStack()->zItem(), number); if (stack->getSize()) throw stack; stack->release(); count -= number; if (count == 0) return; } } } } } if (amount == count || targetSlot->isFull()) targetSlot++; } } InventoryInteraction& InventoryInteraction::operator=(const InventoryInteraction& data) { if (&data == this) return *this; unlock(); current = data.current; other = data.other; dir = data.dir; lock(); return *this; } void InventoryInteraction::endInteraction() { unlock(); current = 0; other = 0; } void InventoryInteraction::pullItems(int count, ItemFilter* zFilter) { if (!current || !other) return; transaction(other, current, zFilter, getOppositeDirection(dir), dir, count); } void InventoryInteraction::pushItems(int count, ItemFilter* zFilter) { if (!current || !other) return; transaction(current, other, zFilter, dir, getOppositeDirection(dir), count); } Inventory::Inventory(const Framework::Vec3 location, bool hasInventory) : ReferenceCounter(), nextSlotId(1), location(location) { if (hasInventory) { pullSlotsOrder = new Framework::RCArray(); pushSlotsOrder = new Framework::RCArray(); itemCache = new Framework::HashMap*>(ITEM_CACHE_SIZE, std::_Identity()); } else { pullSlotsOrder = 0; pushSlotsOrder = 0; itemCache = 0; } } Inventory::~Inventory() { if (pullSlotsOrder) pullSlotsOrder->release(); if (pushSlotsOrder) pushSlotsOrder->release(); if (itemCache) itemCache->release(); } void Inventory::updateCache(ItemSlot* zSlot, int beforeKey) { if (!itemCache) return; int key = zSlot->zStack() ? zSlot->zStack()->zItem()->zItemType()->getId() : -1; if (key == beforeKey) return; if (beforeKey >= 0) { auto tmp = itemCache->safeGet(key, 0); if (tmp) tmp->removeValue(zSlot); } if (zSlot->zStack()) { auto tmp = itemCache->safeGet(key, 0); if (!tmp) { tmp = new Array(); itemCache->put(key, tmp); } tmp->add(zSlot, 0); } } void Inventory::addSlot(ItemSlot* slot) { cs.lock(); ((ItemSlotIDSetter*)slot)->setId(nextSlotId++); int pullPrio = slot->getPullPriority(); int pushPrio = slot->getPushPriority(); int index = 0; for (auto stack : *pullSlotsOrder) { if (stack->getPullPriority() > pullPrio) break; index++; } pullSlotsOrder->add(dynamic_cast(slot->getThis()), index); index = 0; for (auto stack : *pushSlotsOrder) { if (stack->getPushPriority() > pushPrio) break; index++; } pushSlotsOrder->add(slot, index); updateCache(slot, -1); cs.unlock(); } bool Inventory::allowPullStack(ItemSlot* zSlot, Direction dir) const { return pullSlotsOrder; } bool Inventory::allowPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem, int& count) const { return pushSlotsOrder; } void Inventory::afterPullStack(ItemSlot* zSlot, Direction dir, const Item* zItem, int count) {} void Inventory::afterPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem, int count) {} void Inventory::loadInventory(Framework::StreamReader* zReader) { if (itemCache) { for (auto stack : *pushSlotsOrder) { int size = 0; zReader->lese((char*)&size, 4); if (size != 0) { int id = 0; zReader->lese((char*)&id, 4); Item* item = StaticRegistry::INSTANCE.zElement(id)->loadItem(zReader); stack->addItems(new ItemStack(item, size), NO_DIRECTION); } } } } void Inventory::saveInventory(Framework::StreamWriter* zWriter) { if (itemCache) { for (auto slot : *pushSlotsOrder) { const ItemStack* stack = slot->zStack(); int value = 0; if (!stack || !stack->zItem()) { zWriter->schreibe((char*)&value, 4); } else { value = stack->getSize(); zWriter->schreibe((char*)&value, 4); value = stack->zItem()->zItemType()->getId(); zWriter->schreibe((char*)&value, 4); stack->zItem()->zItemType()->saveItem(stack->zItem(), zWriter); } } } } void Inventory::localTransaction(Array< ItemSlot* >* zSourceSlots, Array< ItemSlot* >* zTargetSlots, ItemFilter* zFilter, int count, Direction outDir, Direction inDir) { if (itemCache) { cs.lock(); auto sourceSlot = zSourceSlots ? zSourceSlots->begin() : pullSlotsOrder->begin(); for (auto targetSlot = zTargetSlots->begin(); targetSlot; ) { int amount = count; if (!targetSlot->isFull()) { if (targetSlot->zStack()) { Array* zSurceListe = itemCache->safeGet(targetSlot->zStack()->zItem()->zItemType()->getId(), 0); if (zSurceListe) { Array toDelete; int index = 0; for (auto sourceSlot = zSurceListe->begin(); sourceSlot; sourceSlot++, index++) { if (zSourceSlots && zSourceSlots->getWertIndex(sourceSlot) < 0) continue; if (zFilter && !zFilter->matchItem(sourceSlot->zStack()->zItem())) continue; int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), inDir), count); if (number > 0) { ItemStack* stack = sourceSlot->takeItemsOut(number, outDir); if (stack) { if (!sourceSlot->zStack()) toDelete.add(index, 0); targetSlot->addItems(stack, inDir); updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId()); if (stack->getSize()) { cs.unlock(); throw stack; } stack->release(); count -= number; if (count == 0) break; } } } for (auto indexToDelete = toDelete.begin(); indexToDelete; indexToDelete++) zSurceListe->remove(indexToDelete); if (count == 0) { cs.unlock(); return; } } } else { while (sourceSlot && (!sourceSlot->zStack() || (zFilter && !zFilter->matchItem(sourceSlot->zStack()->zItem())))) sourceSlot++; if (!sourceSlot) { cs.unlock(); return; } int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), inDir), count); if (number > 0) { ItemStack* stack = sourceSlot->takeItemsOut(number, outDir); if (stack) { targetSlot->addItems(stack, inDir); updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId()); updateCache(targetSlot, -1); if (stack->getSize()) { cs.unlock(); throw stack; } stack->release(); count -= number; if (count == 0) { cs.unlock(); return; } } } } } if (amount == count || targetSlot->isFull()) targetSlot++; } cs.unlock(); } } void Inventory::addItems(ItemStack* items, Direction dir) { if (itemCache && items && items->getSize() > 0) { cs.lock(); for (auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++) { if (!targetSlot->isFull()) { if (targetSlot->zStack()) { int number = MIN(targetSlot->numberOfAddableItems(items, dir), items->getSize()); int tmp = number; if (number > 0 && allowPushStack(targetSlot, dir, items->zItem(), tmp)) { number = MIN(number, tmp); ItemStack* stack = items->split(number); if (stack) { targetSlot->addItems(stack, dir); afterPushStack(targetSlot, dir, targetSlot->zStack()->zItem(), number); if (stack->getSize()) throw stack; stack->release(); if (!items->getSize()) break; } } } else { int number = MIN(targetSlot->numberOfAddableItems(items, dir), items->getSize()); int tmp = number; if (number > 0 && allowPushStack(targetSlot, dir, items->zItem(), tmp)) { number = MIN(number, tmp); ItemStack* stack = items->split(number); if (stack) { targetSlot->addItems(stack, dir); updateCache(targetSlot, -1); afterPushStack(targetSlot, dir, targetSlot->zStack()->zItem(), number); if (stack->getSize()) throw stack; stack->release(); if (!items->getSize()) break; } } } } } cs.unlock(); } } ItemStack* Inventory::takeItemsOut(ItemSlot* zSlot, int count, Direction dir) { if (allowPullStack(zSlot, dir)) { ItemStack* stack = zSlot->takeItemsOut(count, dir); if (stack) updateCache(zSlot, stack->zItem()->zItemType()->getId()); return stack; } return 0; } InventoryInteraction Inventory::interactWith(Inventory* zInventory, Direction dir) { return InventoryInteraction(this, zInventory, dir); } void Inventory::unsaveAddItem(ItemStack* zStack, Direction dir) { addItems(zStack, dir); } int Inventory::numberOfAddableItems(const ItemStack* zStack, Direction dir) const { int count = 0; for (auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++) { int maxCount = targetSlot->numberOfAddableItems(zStack, dir); int allowed = maxCount; if (allowPushStack(targetSlot, dir, zStack->zItem(), allowed)) count += MIN(maxCount, allowed); } return count; } Framework::Iterator Inventory::begin() { return pullSlotsOrder->begin(); } Framework::Iterator Inventory::end() { return pullSlotsOrder->end(); }