Inventory.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. #include "Inventory.h"
  2. #include "ItemFilter.h"
  3. #include "Constants.h"
  4. #include "Area.h"
  5. using namespace Framework;
  6. InventoryInteraction::InventoryInteraction( Inventory* current, Inventory* other, Direction dir )
  7. : current( current ),
  8. other( other ),
  9. dir( dir )
  10. {
  11. lock();
  12. }
  13. InventoryInteraction::InventoryInteraction( const InventoryInteraction& interaction )
  14. : InventoryInteraction( interaction.current, interaction.other, interaction.dir )
  15. {}
  16. InventoryInteraction::~InventoryInteraction()
  17. {
  18. unlock();
  19. }
  20. void InventoryInteraction::lock()
  21. {
  22. if( !current || !other ) return;
  23. if( current->location.x < other->location.x )
  24. {
  25. current->cs.lock();
  26. other->cs.lock();
  27. return;
  28. }
  29. else if( current->location.x == other->location.x )
  30. {
  31. if( current->location.y < other->location.y )
  32. {
  33. current->cs.lock();
  34. other->cs.lock();
  35. return;
  36. }
  37. else if( current->location.y == other->location.y )
  38. {
  39. if( current->location.z < other->location.z )
  40. {
  41. current->cs.lock();
  42. other->cs.lock();
  43. return;
  44. }
  45. }
  46. }
  47. other->cs.lock();
  48. current->cs.lock();
  49. }
  50. void InventoryInteraction::unlock()
  51. {
  52. if( !current || !other ) return;
  53. if( current->location.x < other->location.x )
  54. {
  55. current->cs.unlock();
  56. other->cs.unlock();
  57. return;
  58. }
  59. else if( current->location.x == other->location.x )
  60. {
  61. if( current->location.y < other->location.y )
  62. {
  63. current->cs.unlock();
  64. other->cs.unlock();
  65. return;
  66. }
  67. else if( current->location.y == other->location.y )
  68. {
  69. if( current->location.z < other->location.z )
  70. {
  71. current->cs.unlock();
  72. other->cs.unlock();
  73. return;
  74. }
  75. }
  76. }
  77. other->cs.unlock();
  78. current->cs.unlock();
  79. }
  80. void InventoryInteraction::transaction( Inventory* zSource, Inventory* zTarget, ItemFilter* zFilter, Direction sourceView, Direction targetView, int count )
  81. {
  82. // TODO: rewrite this such that for each source slot all target slots are looped trough
  83. auto sourceSlot = zSource->pullSlotsOrder->begin();
  84. for( auto targetSlot = zTarget->pushSlotsOrder->begin(); targetSlot; )
  85. {
  86. int amount = count;
  87. if( !targetSlot->isFull() )
  88. {
  89. if( targetSlot->zStack() )
  90. {
  91. Array<ItemSlot*>* zSurceListe = zSource->itemCache->safeGet( targetSlot->zStack()->zItem()->zItemType()->getId(), 0 );
  92. if( zSurceListe )
  93. {
  94. Array<int> toDelete;
  95. int index = 0;
  96. for( auto sourceSlot = zSurceListe->begin(); sourceSlot; sourceSlot++, index++ )
  97. {
  98. if( zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ) )
  99. continue;
  100. int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), sourceView ), count );
  101. int tmp = number;
  102. if( number > 0 && zSource->allowPullStack( sourceSlot, sourceView ) && zTarget->allowPushStack( targetSlot, targetView, sourceSlot->zStack()->zItem(), tmp ) )
  103. {
  104. number = MIN( number, tmp );
  105. ItemStack* stack = sourceSlot->takeItemsOut( number, dir );
  106. if( stack )
  107. {
  108. if( !sourceSlot->zStack() )
  109. toDelete.add( index, 0 );
  110. targetSlot->addItems( stack, dir );
  111. zSource->updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() );
  112. zSource->afterPullStack( sourceSlot, sourceView, targetSlot->zStack()->zItem(), number );
  113. zTarget->afterPushStack( targetSlot, targetView, targetSlot->zStack()->zItem(), number );
  114. if( stack->getSize() )
  115. throw stack;
  116. stack->release();
  117. count -= number;
  118. if( count == 0 )
  119. break;
  120. }
  121. }
  122. }
  123. for( auto indexToDelete = toDelete.begin(); indexToDelete; indexToDelete++ )
  124. zSurceListe->remove( indexToDelete );
  125. if( count == 0 )
  126. return;
  127. }
  128. }
  129. else
  130. {
  131. while( sourceSlot && (!sourceSlot->zStack() || (zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ))) )
  132. sourceSlot++;
  133. if( !sourceSlot )
  134. return;
  135. int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), sourceView ), count );
  136. int tmp = number;
  137. if( number > 0 && zSource->allowPullStack( sourceSlot, sourceView ) && zTarget->allowPushStack( targetSlot, targetView, sourceSlot->zStack()->zItem(), tmp ) )
  138. {
  139. number = MIN( number, tmp );
  140. if( number > 0 )
  141. {
  142. ItemStack* stack = sourceSlot->takeItemsOut( number, dir );
  143. if( stack )
  144. {
  145. targetSlot->addItems( stack, dir );
  146. zSource->updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() );
  147. zTarget->updateCache( targetSlot, -1 );
  148. zSource->afterPullStack( sourceSlot, sourceView, targetSlot->zStack()->zItem(), number );
  149. zTarget->afterPushStack( targetSlot, targetView, targetSlot->zStack()->zItem(), number );
  150. if( stack->getSize() )
  151. throw stack;
  152. stack->release();
  153. count -= number;
  154. if( count == 0 )
  155. return;
  156. }
  157. }
  158. }
  159. }
  160. }
  161. if( amount == count || targetSlot->isFull() )
  162. targetSlot++;
  163. }
  164. }
  165. InventoryInteraction& InventoryInteraction::operator=( const InventoryInteraction& data )
  166. {
  167. if( &data == this ) return *this;
  168. unlock();
  169. current = data.current;
  170. other = data.other;
  171. dir = data.dir;
  172. lock();
  173. return *this;
  174. }
  175. void InventoryInteraction::endInteraction()
  176. {
  177. unlock();
  178. current = 0;
  179. other = 0;
  180. }
  181. void InventoryInteraction::pullItems( int count, ItemFilter* zFilter )
  182. {
  183. if( !current || !other ) return;
  184. transaction( other, current, zFilter, getOppositeDirection( dir ), dir, count );
  185. }
  186. void InventoryInteraction::pushItems( int count, ItemFilter* zFilter )
  187. {
  188. if( !current || !other ) return;
  189. transaction( current, other, zFilter, dir, getOppositeDirection( dir ), count );
  190. }
  191. Inventory::Inventory( const Framework::Vec3<float> location, bool hasInventory )
  192. : ReferenceCounter(),
  193. location( location )
  194. {
  195. if( hasInventory )
  196. {
  197. pullSlotsOrder = new Framework::RCArray<ItemSlot>();
  198. pushSlotsOrder = new Framework::RCArray<ItemSlot>();
  199. itemCache = new Framework::HashMap<int, Framework::Array<ItemSlot*>*>( ITEM_CACHE_SIZE, std::_Identity<int>() );
  200. }
  201. else
  202. {
  203. pullSlotsOrder = 0;
  204. pushSlotsOrder = 0;
  205. itemCache = 0;
  206. }
  207. }
  208. Inventory::~Inventory()
  209. {
  210. if( pullSlotsOrder )
  211. pullSlotsOrder->release();
  212. if( pushSlotsOrder )
  213. pushSlotsOrder->release();
  214. if( itemCache )
  215. itemCache->release();
  216. }
  217. void Inventory::updateCache( ItemSlot* zSlot, int beforeKey )
  218. {
  219. if( !itemCache )
  220. return;
  221. int key = zSlot->zStack() ? zSlot->zStack()->zItem()->zItemType()->getId() : -1;
  222. if( key == beforeKey )
  223. return;
  224. if( beforeKey >= 0 )
  225. {
  226. auto tmp = itemCache->safeGet( key, 0 );
  227. if( tmp )
  228. tmp->removeValue( zSlot );
  229. }
  230. if( zSlot->zStack() )
  231. {
  232. auto tmp = itemCache->safeGet( key, 0 );
  233. if( !tmp )
  234. {
  235. tmp = new Array<ItemSlot*>();
  236. itemCache->put( key, tmp );
  237. }
  238. tmp->add( zSlot, 0 );
  239. }
  240. }
  241. void Inventory::addSlot( ItemSlot* slot )
  242. {
  243. cs.lock();
  244. int pullPrio = slot->getPullPriority();
  245. int pushPrio = slot->getPushPriority();
  246. int index = 0;
  247. for( auto stack : *pullSlotsOrder )
  248. {
  249. if( stack->getPullPriority() > pullPrio )
  250. break;
  251. index++;
  252. }
  253. pullSlotsOrder->add( dynamic_cast<ItemSlot*>(slot->getThis()), index );
  254. index = 0;
  255. for( auto stack : *pushSlotsOrder )
  256. {
  257. if( stack->getPushPriority() > pushPrio )
  258. break;
  259. index++;
  260. }
  261. pushSlotsOrder->add( slot, index );
  262. updateCache( slot, -1 );
  263. cs.unlock();
  264. }
  265. bool Inventory::allowPullStack( ItemSlot* zSlot, Direction dir ) const
  266. {
  267. return pullSlotsOrder;
  268. }
  269. bool Inventory::allowPushStack( ItemSlot* zSlot, Direction dir, const Item* zItem, int& count ) const
  270. {
  271. return pushSlotsOrder;
  272. }
  273. void Inventory::afterPullStack( ItemSlot* zSlot, Direction dir, const Item* zItem, int count )
  274. {}
  275. void Inventory::afterPushStack( ItemSlot* zSlot, Direction dir, const Item* zItem, int count )
  276. {}
  277. void Inventory::loadInventory( Framework::StreamReader* zReader )
  278. {
  279. if( itemCache )
  280. {
  281. for( auto stack : *pushSlotsOrder )
  282. {
  283. int size = 0;
  284. zReader->lese( (char*)&size, 4 );
  285. if( size != 0 )
  286. {
  287. int id = 0;
  288. zReader->lese( (char*)&id, 4 );
  289. Item* item = StaticRegistry<ItemType>::INSTANCE.zElement( id )->loadItem( zReader );
  290. stack->addItems( new ItemStack( item, size ), NO_DIRECTION );
  291. }
  292. }
  293. }
  294. }
  295. void Inventory::saveInventory( Framework::StreamWriter* zWriter )
  296. {
  297. if( itemCache )
  298. {
  299. for( auto slot : *pushSlotsOrder )
  300. {
  301. const ItemStack* stack = slot->zStack();
  302. int value = 0;
  303. if( !stack || !stack->zItem() )
  304. {
  305. zWriter->schreibe( (char*)&value, 4 );
  306. }
  307. else
  308. {
  309. value = stack->getSize();
  310. zWriter->schreibe( (char*)&value, 4 );
  311. value = stack->zItem()->zItemType()->getId();
  312. zWriter->schreibe( (char*)&value, 4 );
  313. stack->zItem()->zItemType()->saveItem( stack->zItem(), zWriter );
  314. }
  315. }
  316. }
  317. }
  318. void Inventory::localTransaction( Array< ItemSlot* >* zSourceSlots, Array< ItemSlot* >* zTargetSlots, ItemFilter* zFilter, int count )
  319. {
  320. if( itemCache )
  321. {
  322. cs.lock();
  323. auto sourceSlot = zSourceSlots->begin();
  324. for( auto targetSlot = zTargetSlots->begin(); targetSlot; )
  325. {
  326. int amount = count;
  327. if( !targetSlot->isFull() )
  328. {
  329. if( targetSlot->zStack() )
  330. {
  331. Array<ItemSlot*>* zSurceListe = itemCache->safeGet( targetSlot->zStack()->zItem()->zItemType()->getId(), 0 );
  332. if( zSurceListe )
  333. {
  334. Array<int> toDelete;
  335. int index = 0;
  336. for( auto sourceSlot = zSurceListe->begin(); sourceSlot; sourceSlot++, index++ )
  337. {
  338. if( zSourceSlots->getWertIndex( sourceSlot ) < 0 )
  339. continue;
  340. if( zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ) )
  341. continue;
  342. int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), NO_DIRECTION ), count );
  343. if( number > 0 )
  344. {
  345. ItemStack* stack = sourceSlot->takeItemsOut( number, NO_DIRECTION );
  346. if( stack )
  347. {
  348. if( !sourceSlot->zStack() )
  349. toDelete.add( index, 0 );
  350. targetSlot->addItems( stack, NO_DIRECTION );
  351. updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() );
  352. if( stack->getSize() )
  353. {
  354. cs.unlock();
  355. throw stack;
  356. }
  357. stack->release();
  358. count -= number;
  359. if( count == 0 )
  360. break;
  361. }
  362. }
  363. }
  364. for( auto indexToDelete = toDelete.begin(); indexToDelete; indexToDelete++ )
  365. zSurceListe->remove( indexToDelete );
  366. if( count == 0 )
  367. {
  368. cs.unlock();
  369. return;
  370. }
  371. }
  372. }
  373. else
  374. {
  375. while( sourceSlot && (!sourceSlot->zStack() || (zFilter && !zFilter->matchItem( sourceSlot->zStack()->zItem() ))) )
  376. sourceSlot++;
  377. if( !sourceSlot )
  378. {
  379. cs.unlock();
  380. return;
  381. }
  382. int number = MIN( targetSlot->numberOfAddableItems( sourceSlot->zStack(), NO_DIRECTION ), count );
  383. if( number > 0 )
  384. {
  385. ItemStack* stack = sourceSlot->takeItemsOut( number, NO_DIRECTION );
  386. if( stack )
  387. {
  388. targetSlot->addItems( stack, NO_DIRECTION );
  389. updateCache( sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId() );
  390. updateCache( targetSlot, -1 );
  391. if( stack->getSize() )
  392. {
  393. cs.unlock();
  394. throw stack;
  395. }
  396. stack->release();
  397. count -= number;
  398. if( count == 0 )
  399. {
  400. cs.unlock();
  401. return;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. if( amount == count || targetSlot->isFull() )
  408. targetSlot++;
  409. }
  410. cs.unlock();
  411. }
  412. }
  413. void Inventory::addItems( ItemStack* items, Direction dir )
  414. {
  415. if( itemCache && items && items->getSize() > 0 )
  416. {
  417. cs.lock();
  418. for( auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++ )
  419. {
  420. if( !targetSlot->isFull() )
  421. {
  422. if( targetSlot->zStack() )
  423. {
  424. int number = MIN( targetSlot->numberOfAddableItems( items, dir ), items->getSize() );
  425. int tmp = number;
  426. if( number > 0 && allowPushStack( targetSlot, dir, items->zItem(), tmp ) )
  427. {
  428. number = MIN( number, tmp );
  429. ItemStack* stack = items->split( number );
  430. if( stack )
  431. {
  432. targetSlot->addItems( stack, dir );
  433. afterPushStack( targetSlot, dir, targetSlot->zStack()->zItem(), number );
  434. if( stack->getSize() )
  435. throw stack;
  436. stack->release();
  437. if( !items->getSize() )
  438. break;
  439. }
  440. }
  441. }
  442. else
  443. {
  444. int number = MIN( targetSlot->numberOfAddableItems( items, dir ), items->getSize() );
  445. int tmp = number;
  446. if( number > 0 && allowPushStack( targetSlot, dir, items->zItem(), tmp ) )
  447. {
  448. number = MIN( number, tmp );
  449. ItemStack* stack = items->split( number );
  450. if( stack )
  451. {
  452. targetSlot->addItems( stack, dir );
  453. updateCache( targetSlot, -1 );
  454. afterPushStack( targetSlot, dir, targetSlot->zStack()->zItem(), number );
  455. if( stack->getSize() )
  456. throw stack;
  457. stack->release();
  458. if( !items->getSize() )
  459. break;
  460. }
  461. }
  462. }
  463. }
  464. }
  465. cs.unlock();
  466. }
  467. }
  468. ItemStack* Inventory::takeItemsOut( ItemSlot* zSlot, int count, Direction dir )
  469. {
  470. if( allowPullStack( zSlot, dir ) )
  471. {
  472. ItemStack* stack = zSlot->takeItemsOut( count, dir );
  473. if( stack )
  474. updateCache( zSlot, stack->zItem()->zItemType()->getId() );
  475. return stack;
  476. }
  477. return 0;
  478. }
  479. InventoryInteraction Inventory::interactWith( Inventory* zInventory, Direction dir )
  480. {
  481. return InventoryInteraction( this, zInventory, dir );
  482. }
  483. void Inventory::unsaveAddItem( ItemStack* zStack, Direction dir )
  484. {
  485. addItems( zStack, dir );
  486. }
  487. int Inventory::numberOfAddableItems( const ItemStack* zStack, Direction dir ) const
  488. {
  489. int count = 0;
  490. for( auto targetSlot = pushSlotsOrder->begin(); targetSlot; targetSlot++ )
  491. {
  492. int maxCount = targetSlot->numberOfAddableItems( zStack, dir );
  493. int allowed = maxCount;
  494. if( allowPushStack( targetSlot, dir, zStack->zItem(), allowed ) )
  495. count += MIN( maxCount, allowed );
  496. }
  497. return count;
  498. }