#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.Enter(); other->cs.Enter(); return; } else if( current->location.x == other->location.x ) { if( current->location.y < other->location.y ) { current->cs.Enter(); other->cs.Enter(); return; } else if( current->location.y == other->location.y ) { if( current->location.z < other->location.z ) { current->cs.Enter(); other->cs.Enter(); return; } } } other->cs.Enter(); current->cs.Enter(); } void InventoryInteraction::unlock() { if( !current || !other ) return; if( current->location.x < other->location.x ) { current->cs.Leave(); other->cs.Leave(); return; } else if( current->location.x == other->location.x ) { if( current->location.y < other->location.y ) { current->cs.Leave(); other->cs.Leave(); return; } else if( current->location.y == other->location.y ) { if( current->location.z < other->location.z ) { current->cs.Leave(); other->cs.Leave(); return; } } } other->cs.Leave(); current->cs.Leave(); } void InventoryInteraction::transaction( Inventory *zSource, Inventory *zTarget, ItemFilter *zFilter, Direction sourceView, Direction targetView, int count ) { auto sourceSlot = zSource->pullSlotsOrder->getIterator(); for( auto targetSlot = zTarget->pushSlotsOrder->getIterator(); 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->getIterator(); 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->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.getIterator(); 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 ); ItemStack *stack = sourceSlot->takeItemsOut( number, dir ); if( stack ) { zSource->updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() ); targetSlot->addItems( stack, dir ); zTarget->updateCache( targetSlot, 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 ) 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, opposite( dir ), dir, count ); } void InventoryInteraction::pushItems( int count, ItemFilter *zFilter ) { if( !current || !other ) return; transaction( current, other, zFilter, dir, opposite( 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()->zItem()->zItemType()->getId(); 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.Enter(); int pullPrio = slot->getPullPriority(); int pushPrio = slot->getPushPriority(); int index = 0; for( auto iterator = pullSlotsOrder->getIterator(); iterator; iterator++, index++ ) { if( iterator->getPullPriority() > pullPrio ) break; } pullSlotsOrder->add( dynamic_cast(slot->getThis()), index ); index = 0; for( auto iterator = pushSlotsOrder->getIterator(); iterator; iterator++, index++ ) { if( iterator->getPushPriority() > pushPrio ) break; } pullSlotsOrder->add( slot, index ); updateCache( slot, -1 ); cs.Leave(); } bool Inventory::allowPullStack( ItemSlot *zSlot, Direction dir ) { return pullSlotsOrder; } bool Inventory::allowPushStack( ItemSlot *zSlot, Direction dir, const Item *zItem, int &count ) { 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 iterator = pushSlotsOrder->getIterator(); iterator; iterator++ ) { 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 ); iterator->addItems( new ItemStack( item, size ), NO_DIRECTION ); } } } } void Inventory::saveInventory( Framework::StreamWriter *zWriter ) { if( itemCache ) { for( auto iterator = pushSlotsOrder->getIterator(); iterator; iterator++ ) { const ItemStack *stack = iterator->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.Enter(); auto sourceSlot = zSourceSlots->getIterator(); for( auto targetSlot = zTargetSlots->getIterator(); 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->getIterator(); 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 ); if( stack->getSize() ) { cs.Leave(); throw stack; } stack->release(); count -= number; if( count == 0 ) break; } } } for( auto indexToDelete = toDelete.getIterator(); indexToDelete; indexToDelete++ ) zSurceListe->remove( indexToDelete ); if( count == 0 ) { cs.Leave(); return; } } } else { while( sourceSlot && (!sourceSlot->zStack() || (zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ))) ) sourceSlot++; if( !sourceSlot ) { cs.Leave(); return; } int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), NO_DIRECTION ), count ); if( number > 0 ) { ItemStack *stack = sourceSlot->takeItemsOut( number, NO_DIRECTION ); if( stack ) { updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() ); targetSlot->addItems( stack, NO_DIRECTION ); updateCache( targetSlot, targetSlot->zStack()->zItem()->zItemType()->getId() ); if( stack->getSize() ) { cs.Leave(); throw stack; } stack->release(); count -= number; if( count == 0 ) { cs.Leave(); return; } } } } } if( amount == count || targetSlot->isFull() ) targetSlot++; } cs.Leave(); } } InventoryInteraction Inventory::interactWith( Inventory *zInventory, Direction dir ) { return InventoryInteraction( this, zInventory, dir ); }