123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 |
- #include <InMemoryBuffer.h>
- #include "Inventory.h"
- #include "ItemFilter.h"
- #include "Constants.h"
- #include "Area.h"
- #include "NetworkMessage.h"
- #include "Entity.h"
- #include "Game.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)
- {
- for (auto sourceSlot = zSource->pullSlotsOrder->begin(); sourceSlot; )
- {
- while (sourceSlot && (sourceSlot->getNumberOfItems() == 0 || (zFilter && !zFilter->matchSourceSlot(sourceSlot))))
- sourceSlot++;
- if (!sourceSlot) break;
- // TODO: use target cache ot get list of slots that already contains the source item
- bool needNext = 1;
- for (auto targetSlot = zTarget->pushSlotsOrder->begin(); targetSlot; )
- {
- while (targetSlot && (targetSlot->isFull() || (zFilter && !zFilter->matchTargetSlot(targetSlot))))
- targetSlot++;
- if (!targetSlot) break;
- needNext &= !Inventory::unsafeMove(zSource, zTarget, sourceSlot, targetSlot, sourceView, targetView, count);
- if (count == 0)
- return;
- if (sourceSlot->getNumberOfItems() == 0)
- break;
- }
- if (needNext)
- sourceSlot++;
- }
- }
- 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<float> location, bool hasInventory)
- : ReferenceCounter(),
- nextSlotId(1),
- location(location)
- {
- if (hasInventory)
- {
- pullSlotsOrder = new Framework::RCArray<ItemSlot>();
- pushSlotsOrder = new Framework::RCArray<ItemSlot>();
- itemCache = new Framework::HashMap<int, Framework::Array<ItemSlot*>*>(ITEM_CACHE_SIZE, std::_Identity<int>());
- }
- 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<ItemSlot*>();
- 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<ItemSlot*>(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)
- {
- NetworkMessage* msg = new NetworkMessage();
- char* message = new char[9];
- message[0] = 1; // set count of items
- *(int*)(message + 1) = zSlot->getId();
- *(int*)(message + 5) = zSlot->getNumberOfItems();
- msg->setMessage(message, 9);
- notyObservers(msg);
- for (auto call : afterPullStackCalls)
- call(zSlot, dir, zItem, count);
- }
- void Inventory::afterPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem, int count)
- {
- if (zSlot->getNumberOfItems() > count)
- {
- NetworkMessage* msg = new NetworkMessage();
- char* message = new char[9];
- message[0] = 1; // set count of items
- *(int*)(message + 1) = zSlot->getId();
- *(int*)(message + 5) = zSlot->getNumberOfItems();
- msg->setMessage(message, 9);
- notyObservers(msg);
- }
- else
- {
- NetworkMessage* msg = new NetworkMessage();
- char* message = new char[29];
- message[0] = 2; // add new stack
- *(int*)(message + 1) = zSlot->getId();
- *(int*)(message + 5) = zSlot->getNumberOfItems();
- const Item* zItem = zSlot->zStack()->zItem();
- *(float*)(message + 9) = zItem->getHp();
- *(float*)(message + 13) = zItem->getMaxHp();
- *(float*)(message + 17) = zItem->getDurability();
- *(float*)(message + 21) = zItem->getMaxDurability();
- *(int*)(message + 25) = zItem->zItemType()->getId();
- msg->setMessage(message, 29);
- notyObservers(msg);
- }
- for (auto call : afterPushStackCalls)
- call(zSlot, dir, zItem, 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<ItemType>::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::notyObservers(NetworkMessage* msg)
- {
- cs.lock();
- int index = 0;
- Array<int> toDelete;
- for (auto observer : observers)
- {
- Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
- if (e)
- {
- msg->addressGui(observer.getSecond());
- Game::INSTANCE->sendMessage(msg->clone(), e);
- }
- else
- toDelete.add(index, 0);
- index++;
- }
- for (int i : toDelete)
- observers.remove(i);
- cs.unlock();
- msg->release();
- }
- void Inventory::removeObserver(Entity* zSource, Framework::Text id)
- {
- cs.lock();
- int index = 0;
- for (auto observer : observers)
- {
- if (observer.getFirst() == zSource->getId() && observer.getSecond().istGleich(id))
- {
- observers.remove(index);
- break;
- }
- index++;
- }
- cs.unlock();
- }
- void Inventory::addObserver(Entity* zSource, Framework::Text id)
- {
- cs.lock();
- for (auto observer : observers)
- {
- if (observer.getFirst() == zSource->getId() && observer.getSecond().istGleich(id))
- {
- cs.unlock();
- return;
- }
- }
- observers.add(ImmutablePair<int, Text>(zSource->getId(), id));
- cs.unlock();
- for (auto call : observerAddedCalls)
- call(zSource, id);
- }
- const ItemSlot* Inventory::zSlot(int id) const
- {
- if (itemCache)
- {
- for (auto slot : *pushSlotsOrder)
- {
- if (slot->getId() == id)
- return slot;
- }
- }
- return 0;
- }
- 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();
- while (true)
- {
- while (sourceSlot && (sourceSlot->getNumberOfItems() == 0 || (zFilter && !zFilter->matchSourceSlot(sourceSlot))))
- sourceSlot++;
- if (!sourceSlot)
- {
- cs.unlock();
- return;
- }
- bool needNext = 1;
- for (auto targetSlot = zTargetSlots->begin(); targetSlot; )
- {
- while (targetSlot && (targetSlot->isFull() || (zFilter && !zFilter->matchTargetSlot(targetSlot))))
- targetSlot++;
- if (!targetSlot) break;
- needNext &= !Inventory::unsafeMove(this, this, sourceSlot, targetSlot, outDir, inDir, count);
- if (count == 0)
- {
- cs.unlock();
- return;
- }
- if (sourceSlot->getNumberOfItems() == 0)
- break;
- }
- if (needNext)
- sourceSlot++;
- }
- cs.unlock();
- }
- }
- void Inventory::addItems(ItemStack* zItems, Direction dir)
- {
- if (itemCache && zItems && zItems->getSize() > 0)
- {
- cs.lock();
- for (auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++)
- {
- if (!targetSlot->isFull())
- {
- if (targetSlot->zStack())
- {
- if (targetSlot->zStack()->zItem()->canBeStackedWith(zItems->zItem()))
- {
- int number = MIN(targetSlot->numberOfAddableItems(zItems, dir), zItems->getSize());
- int tmp = number;
- if (number > 0 && allowPushStack(targetSlot, dir, zItems->zItem(), tmp))
- {
- number = MIN(number, tmp);
- ItemStack* stack = zItems->split(number);
- if (stack)
- {
- targetSlot->addItems(stack, dir);
- afterPushStack(targetSlot, dir, targetSlot->zStack()->zItem(), number);
- if (stack->getSize())
- throw stack;
- stack->release();
- if (!zItems->getSize())
- break;
- }
- }
- }
- }
- else
- {
- int number = MIN(targetSlot->numberOfAddableItems(zItems, dir), zItems->getSize());
- int tmp = number;
- if (number > 0 && allowPushStack(targetSlot, dir, zItems->zItem(), tmp))
- {
- number = MIN(number, tmp);
- ItemStack* stack = zItems->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 (!zItems->getSize())
- break;
- }
- }
- }
- }
- }
- cs.unlock();
- }
- }
- void Inventory::addItems(ItemSlot* zSlot, ItemStack* zItems, Direction dir)
- {
- if (zSlot->zStack() && !zSlot->zStack()->zItem()->canBeStackedWith(zItems->zItem()))
- return;
- bool needUpdate = !zSlot->zStack();
- int number = MIN(zSlot->numberOfAddableItems(zItems, dir), zItems->getSize());
- int tmp = number;
- if (number > 0 && allowPushStack(zSlot, dir, zItems->zItem(), tmp))
- {
- number = MIN(number, tmp);
- ItemStack* stack = zItems->split(number);
- if (stack)
- {
- zSlot->addItems(stack, dir);
- if (needUpdate)
- updateCache(zSlot, -1);
- afterPushStack(zSlot, dir, zSlot->zStack()->zItem(), number);
- if (stack->getSize())
- throw stack;
- stack->release();
- }
- }
- }
- 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());
- if (stack->getSize() > 0)
- afterPullStack(zSlot, dir, stack->zItem(), stack->getSize());
- }
- 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<ItemSlot*> Inventory::begin()
- {
- return pullSlotsOrder->begin();
- }
- Framework::Iterator<ItemSlot*> Inventory::end()
- {
- return pullSlotsOrder->end();
- }
- void Inventory::inventoryApi(Framework::StreamReader* zRequest, NetworkMessage* zResponse, Entity* zSource)
- {
- char type;
- zRequest->lese(&type, 1);
- switch (type)
- {
- case 0: // request inventory
- {
- char idLen;
- zRequest->lese(&idLen, 1);
- char* id = new char[idLen + 1];
- zRequest->lese(id, idLen);
- id[(int)idLen] = 0;
- zResponse->addressGui(id);
- addObserver(zSource, id);
- delete[] id;
- char filterLen;
- zRequest->lese(&filterLen, 1);
- char* filter = new char[filterLen + 1];
- zRequest->lese(filter, filterLen);
- filter[(int)filterLen] = 0;
- InMemoryBuffer buffer;
- int count = 0;
- for (ItemSlot* slot : *this)
- {
- if (slot->getName().istGleich(filter))
- {
- count++;
- int id = slot->getId();
- buffer.schreibe((char*)&id, 4);
- int itemCount = slot->getNumberOfItems();
- buffer.schreibe((char*)&itemCount, 4);
- if (itemCount > 0)
- {
- float f = slot->zStack()->zItem()->getHp();
- buffer.schreibe((char*)&f, 4);
- f = slot->zStack()->zItem()->getMaxHp();
- buffer.schreibe((char*)&f, 4);
- f = slot->zStack()->zItem()->getDurability();
- buffer.schreibe((char*)&f, 4);
- f = slot->zStack()->zItem()->getMaxDurability();
- buffer.schreibe((char*)&f, 4);
- int id = slot->zStack()->zItem()->zItemType()->getId();
- buffer.schreibe((char*)&id, 4);
- }
- }
- }
- delete[] filter;
- char* msg = new char[5 + buffer.getSize()];
- msg[0] = 0;
- *(int*)(msg + 1) = count;
- buffer.lese(msg + 5, (int)buffer.getSize());
- zResponse->setMessage(msg, 5 + (int)buffer.getSize());
- break;
- }
- case 1: // remove Observer
- {
- char idLen;
- zRequest->lese(&idLen, 1);
- char* id = new char[idLen + 1];
- zRequest->lese(id, idLen);
- id[(int)idLen] = 0;
- removeObserver(zSource, id);
- delete[] id;
- break;
- }
- case 2: // request item tooltip
- {
- char idLen;
- zRequest->lese(&idLen, 1);
- char* id = new char[idLen + 1];
- zRequest->lese(id, idLen);
- id[(int)idLen] = 0;
- zResponse->addressGui(id);
- delete[] id;
- int slotId;
- zRequest->lese((char*)&slotId, 4);
- Text uiml;
- for (ItemSlot* slot : *pullSlotsOrder)
- {
- if (slot->getId() == slotId)
- {
- if (slot->zStack() && slot->zStack()->zItem())
- uiml = slot->zStack()->zItem()->getTooltipUIML();
- }
- }
- short len = (short)uiml.getLength();
- char* buffer = new char[uiml.getLength() + 7];
- buffer[0] = 3;
- *(int*)(buffer + 1) = slotId;
- *(short*)(buffer + 5) = len;
- memcpy(buffer + 7, uiml, len);
- zResponse->setMessage(buffer, len + 7);
- break;
- }
- }
- }
- void Inventory::registerAfterPullStackCall(std::function<void(ItemSlot* zSlot, Direction dir, const Item* zItem, int count)> call)
- {
- afterPullStackCalls.add(call);
- }
- void Inventory::registerAfterPushStackCall(std::function<void(ItemSlot* zSlot, Direction dir, const Item* zItem, int count)> call)
- {
- afterPushStackCalls.add(call);
- }
- void Inventory::registerObserverAddedCall(std::function<void(Entity* zSource, Framework::Text id)> call)
- {
- observerAddedCalls.add(call);
- }
- bool Inventory::unsafeMove(Inventory* zSource, Inventory* zTarget, Iterator<ItemSlot*>& sourceSlot, Iterator<ItemSlot*>& targetSlot, Direction outDir, Direction inDir, int& count)
- {
- if (targetSlot->zStack())
- {
- if (sourceSlot->zStack()->zItem()->canBeStackedWith(targetSlot->zStack()->zItem()))
- {
- int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), outDir), count);
- int tmp = number;
- if (number > 0 && zSource->allowPullStack(sourceSlot, outDir) && zTarget->allowPushStack(targetSlot, inDir, sourceSlot->zStack()->zItem(), tmp))
- {
- number = MIN(number, tmp);
- ItemStack* stack = sourceSlot->takeItemsOut(number, outDir);
- if (stack)
- {
- targetSlot->addItems(stack, inDir);
- zSource->updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId());
- zSource->afterPullStack(sourceSlot, outDir, targetSlot->zStack()->zItem(), number);
- zTarget->afterPushStack(targetSlot, inDir, targetSlot->zStack()->zItem(), number);
- if (stack->getSize())
- throw stack;
- stack->release();
- count -= number;
- return 1;
- }
- else
- targetSlot++;
- }
- else
- targetSlot++;
- }
- else
- targetSlot++;
- }
- else
- {
- int number = MIN(targetSlot->numberOfAddableItems(sourceSlot->zStack(), outDir), count);
- int tmp = number;
- if (number > 0 && zSource->allowPullStack(sourceSlot, outDir) && zTarget->allowPushStack(targetSlot, inDir, sourceSlot->zStack()->zItem(), tmp))
- {
- number = MIN(number, tmp);
- if (number > 0)
- {
- ItemStack* stack = sourceSlot->takeItemsOut(number, outDir);
- if (stack)
- {
- targetSlot->addItems(stack, inDir);
- zSource->updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId());
- zTarget->updateCache(targetSlot, -1);
- zSource->afterPullStack(sourceSlot, outDir, targetSlot->zStack()->zItem(), number);
- zTarget->afterPushStack(targetSlot, inDir, targetSlot->zStack()->zItem(), number);
- if (stack->getSize())
- throw stack;
- stack->release();
- count -= number;
- return 1;
- }
- else
- targetSlot++;
- }
- else
- targetSlot++;
- }
- else
- targetSlot++;
- }
- return 0;
- }
|