#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(), 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(); 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 ) { if( itemCache ) { cs.lock(); auto sourceSlot = zSourceSlots->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->getWertIndex( sourceSlot ) < 0 ) continue; if( zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ) ) continue; int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), NO_DIRECTION ), count ); if( number > 0 ) { ItemStack* stack = sourceSlot->takeItemsOut( number, NO_DIRECTION ); if( stack ) { if( !sourceSlot->zStack() ) toDelete.add( index, 0 ); targetSlot->addItems( stack, NO_DIRECTION ); 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(), NO_DIRECTION ), count ); if( number > 0 ) { ItemStack* stack = sourceSlot->takeItemsOut( number, NO_DIRECTION ); if( stack ) { targetSlot->addItems( stack, NO_DIRECTION ); 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; }