inventory.c 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  1. //-------------------------------------------------------
  2. enum InventoryCommandType
  3. {
  4. MOVE, ///< generic move, may involve animations
  5. SYNC_MOVE, ///< synchronous move. action is finished immeadiately, no animations involved
  6. HAND_EVENT, ///< event for hands
  7. SWAP, ///< swap two entities (simple swap of compatible objects)
  8. FORCESWAP, ///< Forced swap two entities. First goes to second's place, second goes "somewhere else"
  9. DESTROY, ///< destroy of entity right in inventory
  10. REPLACE, ///< replace of entity in inventory (@NOTE: hands goes through HAND_EVENT)
  11. USER_RESERVATION_CANCEL ///< Clear user reserved inventory space
  12. };
  13. enum InventoryJunctureType
  14. {
  15. TAKE, ///< taking from ground
  16. SWAP, ///< swapping from ground
  17. //LOAD, ///< load mag from ground
  18. };
  19. //! NOTE: PREDICTIVE is not to be used at all in multiplayer
  20. enum InventoryMode
  21. {
  22. PREDICTIVE, ///< 'Predictive' means that the operation uses Client-Side Prediction, i.e. the action runs the same code on both, client AND server
  23. LOCAL, ///< 'Local' operation executes from where it is run
  24. JUNCTURE, ///< 'Juncture' operation is used whenever there is possibility of race condition, i.e. two players picking same item from ground
  25. SERVER, ///< 'Server' mode operation is required if and only if the operation runs only on server (creates and/or destroys objects)
  26. };
  27. enum InventoryValidationResult
  28. {
  29. FAILED,
  30. JUNCTURE,
  31. SUCCESS
  32. };
  33. enum InventoryValidationReason
  34. {
  35. UNKNOWN,
  36. JUNCTURE_DENIED,
  37. DROP_PREVENTED
  38. };
  39. class InventoryValidation
  40. {
  41. bool m_IsJuncture = false;
  42. bool m_IsRemote = false;
  43. InventoryValidationResult m_Result = InventoryValidationResult.FAILED;
  44. InventoryValidationReason m_Reason = InventoryValidationReason.UNKNOWN;
  45. bool IsAuthoritative()
  46. {
  47. return !m_IsJuncture && !m_IsRemote;
  48. }
  49. };
  50. enum InventoryCheckContext
  51. {
  52. DEFAULT,
  53. SYNC_CHECK,
  54. }
  55. enum FindInventoryReservationMode
  56. {
  57. //! The original logic, finds anything depending on the parameters, item or dst required
  58. LEGACY,
  59. //! Find a reservation for the item EXCLUDING the dst, item and dst required
  60. ITEM,
  61. //! Find a reservation for the dst EXCLUDING the item, item and dst required
  62. DST,
  63. //! Find an exact reservation for item and dst, item and dst required
  64. EQUAL,
  65. };
  66. /**@class GameInventory
  67. * @brief script counterpart to engine's class Inventory
  68. **/
  69. class GameInventory
  70. {
  71. protected static int m_inventory_check_context = InventoryCheckContext.DEFAULT;
  72. //-------------------------------------------------------
  73. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  74. ///@{ Engine native functions
  75. /**
  76. * @fn GetInventoryOwner
  77. * @return entity associated with this inventory
  78. **/
  79. proto native EntityAI GetInventoryOwner();
  80. #ifdef DEVELOPER
  81. /**
  82. * @fn DumpInventoryDebug
  83. * @brief dump out failed inventory operations of the current inventory
  84. **/
  85. proto native void DumpInventoryDebug();
  86. /**
  87. * @fn DumpStaticInventoryDebug
  88. * @brief dump out a more global list of failed inventory operations
  89. **/
  90. static proto native void DumpStaticInventoryDebug();
  91. #endif
  92. /**
  93. * @fn HasEntityInInventory
  94. * @brief query inventory if item is somewhere
  95. *
  96. * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are
  97. * traversed recursively from root to leafs.
  98. *
  99. * @param[in] item \p item to be found in inventory
  100. * @return true if found, false otherwise
  101. **/
  102. proto native bool HasEntityInInventory(notnull EntityAI item);
  103. /**
  104. * @fn EnumerateInventory
  105. * @brief enumerate inventory using traversal type and filling items array
  106. * @param[in] tt used traversal type
  107. * @param[out] items items in inventory (in order determined by traversal)
  108. * @return true if found any, false otherwise
  109. **/
  110. proto native bool EnumerateInventory(InventoryTraversalType tt, out array<EntityAI> items);
  111. /**
  112. * @fn CountInventory
  113. * @brief almost identical to EnumerateInventory except it does not return items
  114. * @return number of items
  115. **/
  116. proto native int CountInventory();
  117. ///@{ cargo
  118. proto native CargoBase GetCargo();
  119. proto native CargoBase GetCargoFromIndex(int index);
  120. /**
  121. * @brief Create Entity of specified type in cargo of entity
  122. **/
  123. proto native EntityAI CreateEntityInCargo(string typeName);
  124. /**
  125. * @brief Create Entity of specified type in cargo of entity at coordinates (row, col)
  126. * @param typeName type to create
  127. * @param row the row of cargo to be created at
  128. * @param col the column of cargo to be created at
  129. **/
  130. proto native EntityAI CreateEntityInCargoEx(string typeName, int idx, int row, int col, bool flip);
  131. proto native bool HasEntityInCargo(notnull EntityAI e);
  132. proto native bool HasEntityInCargoEx(notnull EntityAI e, int idx, int row, int col);
  133. proto native bool CanAddEntityInCargo(notnull EntityAI e, bool flip);
  134. proto native bool CanAddEntityInCargoEx(notnull EntityAI e, int idx, int row, int col, bool flip);
  135. proto native bool CanAddEntityInCargoExLoc(InventoryLocation loc);
  136. proto native bool TestAddEntityInCargoEx(notnull EntityAI e, int idx, int row, int col, bool flip, bool do_resevation_check, bool do_item_check, bool do_lock_check, bool do_occupancy_test, bool do_script_check, bool do_script_load_check);
  137. proto native bool TestAddEntityInCargoExLoc(notnull InventoryLocation loc, bool do_resevation_check, bool do_item_check, bool do_lock_check, bool do_occupancy_test, bool do_script_check, bool do_script_load_check);
  138. //proto native bool AddEntityInCargo (notnull EntityAI owner, EntityAI cargo);
  139. //proto native bool AddEntityInCargoEx (notnull EntityAI owner, notnull EntityAI e, int idx, int row, int col);
  140. proto native bool CanRemoveEntityInCargo(notnull EntityAI e);
  141. proto native bool CanRemoveEntityInCargoEx(notnull EntityAI e, int idx, int row, int col);
  142. ///@} cargo
  143. ///@{ attachments
  144. /**@fn GetSlotId
  145. * @param index index into array of configured slots (@see GetSlotIdCount)
  146. * @return slot where this item belongs
  147. **/
  148. proto native int GetSlotId(int index);
  149. /**@fn GetSlotIdCount
  150. * @return number of slots this item can belong to
  151. **/
  152. proto native int GetSlotIdCount();
  153. /**@fn GetAttachmentSlotId
  154. * @param index index of the slot for attachment (@see GetAttachmentSlotsCount)
  155. * @return slot for attachment
  156. **/
  157. proto native int GetAttachmentSlotId(int index);
  158. /**@fn GetAttachmentSlotsCount
  159. * @return number of slots for attachments
  160. **/
  161. proto native int GetAttachmentSlotsCount();
  162. /**@fn HasAttachmentSlot
  163. * @return true if this entity has attachments slot with id=slotId
  164. **/
  165. /*proto native*/bool HasAttachmentSlot(int slotId) //TODO - flip to code
  166. {
  167. int count = GetAttachmentSlotsCount();
  168. for (int i = 0; i < count; i++)
  169. {
  170. if (GetAttachmentSlotId(i) == slotId)
  171. return true;
  172. }
  173. return false;
  174. }
  175. /**@fn HasInventorySlot
  176. * @return true if this entity has inventory slot with id=slotId
  177. **/
  178. proto native bool HasInventorySlot(int slotId);
  179. /**@fn AttachmentCount
  180. * @brief Returns count of attachments attached to this item
  181. **/
  182. proto native int AttachmentCount();
  183. /**@fn CreateAttachment
  184. * @brief Create Entity of specified type as attachment of entity
  185. **/
  186. proto native EntityAI CreateAttachment(string typeName);
  187. /**@fn CreateAttachmentEx
  188. * @brief Create Entity of specified type as attachment of entity
  189. * @param typeName type to create
  190. * @param slotId the slot to be created in
  191. **/
  192. proto native EntityAI CreateAttachmentEx(string typeName, int slotId);
  193. /**@fn GetAttachmentFromIndex
  194. * @return attached entity by attachment index
  195. * @NOTE: index is not slot! for getting attachment by slot use FindAttachment
  196. **/
  197. proto native EntityAI GetAttachmentFromIndex(int index);
  198. /**
  199. * @brief Returns attached entity in slot (you can use InventorySlots.GetSlotIdFromString(name) to get slot id)
  200. **/
  201. proto native EntityAI FindAttachment(int slot);
  202. /**
  203. * @brief Returns attached entity in slot (you can use EntityAI.GetActionComponentName to get slot id)
  204. **/
  205. proto native EntityAI FindAttachmentByName(string slotName);
  206. /**@fn HasAttachment
  207. * @brief brief Returns True if entity is attached to this
  208. **/
  209. proto native bool HasAttachment(notnull EntityAI e);
  210. /**@fn HasAttachmentEx
  211. * @brief brief Returns True if entity is attached to this in slot
  212. **/
  213. proto native bool HasAttachmentEx(notnull EntityAI e, int slot);
  214. /**@fn CanAddAttachment
  215. * @brief Check if attachment can be added to any slot
  216. * @return true if entity can be added as attachment
  217. **/
  218. proto native bool CanAddAttachment(notnull EntityAI e);
  219. /**@fn CanAddAttachmentEx
  220. * @brief Check if attachment can be added to slot
  221. * @NOTE: Note that slot index IS NOT slot ID! Slot ID is defined in DZ/data/config.cpp
  222. **/
  223. proto native bool CanAddAttachmentEx(notnull EntityAI e, int slot);
  224. proto native bool CanRemoveAttachment(EntityAI attachment);
  225. proto native bool CanRemoveAttachmentEx(EntityAI attachment, int slot);
  226. //proto native bool RemoveAttachment(EntityAI attachment);
  227. //proto native bool RemoveAttachmentEx(EntityAI attachment, int slot);
  228. /**
  229. * @brief Returns placeholder entity for slot (naked arms, legs etc)
  230. **/
  231. proto native EntityAI FindPlaceholderForSlot(int slot);
  232. proto native bool IsPlaceholderEntity(notnull Object e);
  233. ///@} attachments
  234. /**
  235. * @fn GetCurrentInventoryLocation
  236. * @brief returns information about current item location
  237. * @param[out] loc \p current InventoryLocation
  238. * @return true if current location retrieved (and is valid)
  239. **/
  240. proto native bool GetCurrentInventoryLocation(out notnull InventoryLocation loc);
  241. /**
  242. * @brief FindFreeLocationFor
  243. * @param[in] item item that would be placed in inventory
  244. * @param[out] loc the InventoryLocation the item can be placed to
  245. * @return true if found any, false otherwise
  246. **/
  247. proto native bool FindFreeLocationFor(notnull EntityAI item, FindInventoryLocationType flags, out notnull InventoryLocation loc);
  248. /**
  249. * @brief FindFreeLocationForEx
  250. * @param[in] item item that would be placed in inventory
  251. * @param[out] loc the InventoryLocation the item can be placed to
  252. * @return true if found any, false otherwise
  253. **/
  254. proto native bool FindFreeLocationForEx(notnull EntityAI item, FindInventoryLocationType flags, notnull InventoryLocation exclude, out notnull InventoryLocation loc);
  255. /**
  256. * @brief FindFirstFreeLocationForNewEntity
  257. * @param[in] item_type item type that would be placed in inventory
  258. * @param[out] loc the InventoryLocation the item can be placed to
  259. * @return true if found any, false otherwise
  260. **/
  261. proto native bool FindFirstFreeLocationForNewEntity(string item_type, FindInventoryLocationType flags, out notnull InventoryLocation loc);
  262. /**@fn FindFreeLocationsFor
  263. * @brief searches inventory for suitable location for @item
  264. * @param[in] item item that would be placed in inventory
  265. * @param[in] flags flags affecting selection ( @see FindInventoryLocation )
  266. * @param[out] locs array of InventoryLocations the item can be placed to
  267. * @return number of suitable inventory locations
  268. *
  269. * Example:
  270. * @code
  271. * Entity bino = GetGame().CreateObject("Binoculars", "3312 0 854");
  272. * EntityAI bino2;
  273. * if (Class.CastTo(bino2, bino))
  274. * {
  275. * array<ref InventoryLocation> a = new array<ref InventoryLocation>; // 1) allocate memory for array
  276. * for (int i = 0; i < 10; ++i)
  277. * {
  278. * a.Insert(new InventoryLocation); // 2) pre-allocate inventorylocations from script. engine only fills them in, engine does not allocate memory to avoid mixing.
  279. * }
  280. *
  281. * int sz = player.FindFreeLocationsFor(bino, FindInventoryLocationType.ANY_CARGO | FindInventoryLocationType.ATTACHMENT, a); // 3) ask engine to fill the locations
  282. * if (sz > 0)
  283. * {
  284. * InventoryLocation src = new InventoryLocation;
  285. * bino2.GetCurrentInventoryLocation(src); // 5) get current location
  286. *
  287. * InventoryLocation dst = a[0]; // 6) get first (this is only example)
  288. *
  289. * ... use src and dst
  290. * }
  291. * }
  292. * @endcode
  293. **/
  294. proto native int FindFreeLocationsFor(notnull EntityAI item, FindInventoryLocationType flags, out notnull array<ref InventoryLocation> locs);
  295. /**
  296. * @fn LocationCreateEntity
  297. * @brief creates new item directly at location
  298. * @param[in] type \p item type to be placed in inventory
  299. * @return created entity, null otherwise
  300. **/
  301. static proto native EntityAI LocationCreateEntity(notnull InventoryLocation inv_loc, string type, int iSetupFlags, int iRotation);
  302. /**
  303. * @fn LocationCreateLocalEntity
  304. * @brief creates new <b>local</b> item directly at location
  305. * @NOTE: the item is created localy, i.e. it's not registered to network
  306. * @param[in] type \p item type to be placed in inventory
  307. * @return created entity, null otherwise
  308. **/
  309. static proto native EntityAI LocationCreateLocalEntity(notnull InventoryLocation inv_loc, string type, int iSetupFlags, int iRotation);
  310. /**
  311. * @fn LocationCanAddEntity
  312. * @brief queries if the entity contained in inv_loc.m_item can be added to ground/attachment/cargo/hands/...
  313. * @return true if can be added, false otherwise
  314. **/
  315. static proto native bool LocationCanAddEntity(notnull InventoryLocation inv_loc);
  316. //Added script check to LocationCanAddEntity
  317. static bool LocationCanAddEntityEx(notnull InventoryLocation inv_loc)
  318. {
  319. return LocationCanAddEntity(inv_loc);
  320. }
  321. /**
  322. * @fn LocationTestAddEntity
  323. * @brief test if the entity contained in inv_loc.m_item can be added to ground/attachment/cargo/hands/...
  324. * @return true if can be added, false otherwise
  325. **/
  326. static proto native bool LocationTestAddEntity(notnull InventoryLocation inv_loc, bool do_resevation_check, bool do_item_check, bool do_lock_check, bool do_occupancy_test, bool do_script_check, bool do_script_load_check);
  327. /**
  328. * @fn LocationCanRemoveEntity
  329. * @brief queries if the entity contained in inv_loc.m_item can be removed from ground/attachment/cargo/hands/...
  330. * @return true if can be added, false otherwise
  331. **/
  332. static proto native bool LocationCanRemoveEntity(notnull InventoryLocation inv_loc);
  333. /**
  334. * @fn LocationCanMoveEntity
  335. * @brief queries if the entity contained in inv_loc.m_item can be moved to another location
  336. * This is a shorthand for CanRemove + CanAdd query
  337. * @return true if can be moved, false otherwise
  338. **/
  339. static proto native bool LocationCanMoveEntity(notnull InventoryLocation src, notnull InventoryLocation dst);
  340. static int GetInventoryCheckContext()
  341. {
  342. return m_inventory_check_context;
  343. }
  344. static bool LocationCanMoveEntitySyncCheck(notnull InventoryLocation src, notnull InventoryLocation dst)
  345. {
  346. m_inventory_check_context = InventoryCheckContext.SYNC_CHECK;
  347. bool result = LocationCanMoveEntity(src, dst);
  348. m_inventory_check_context = InventoryCheckContext.DEFAULT;
  349. return result;
  350. }
  351. /**
  352. * @fn LocationCanAddEntity
  353. * @brief query the entity at the specific inventory location
  354. * @return entity at the location, null otherwise
  355. **/
  356. static proto native EntityAI LocationGetEntity(notnull InventoryLocation inv_loc);
  357. //! Returns true if this Inventory owner is in cargo of something
  358. bool IsInCargo()
  359. {
  360. InventoryLocation lcn = new InventoryLocation();
  361. GetCurrentInventoryLocation(lcn);
  362. if (lcn.GetType() == InventoryLocationType.CARGO)
  363. {
  364. return true;
  365. }
  366. return false;
  367. }
  368. //! Returns true if this Inventory owner is an attachment of something
  369. bool IsAttachment()
  370. {
  371. InventoryLocation lcn = new InventoryLocation();
  372. GetCurrentInventoryLocation(lcn);
  373. if (lcn.GetType() == InventoryLocationType.ATTACHMENT)
  374. {
  375. return true;
  376. }
  377. return false;
  378. }
  379. //! Returns true if inventory owner or his hiearchy parents are in cargo
  380. bool IsCargoInHiearchy()
  381. {
  382. InventoryLocation lcn = new InventoryLocation();
  383. EntityAI ent = GetInventoryOwner();
  384. while (ent)
  385. {
  386. if (ent.GetInventory().GetCurrentInventoryLocation(lcn) && lcn.IsValid())
  387. {
  388. if (lcn.GetType() == InventoryLocationType.CARGO || lcn.GetType() == InventoryLocationType.PROXYCARGO)
  389. return true;
  390. }
  391. ent = ent.GetHierarchyParent();
  392. }
  393. return false;
  394. }
  395. //! Returns true if item is considered reachable within inventory
  396. bool AreChildrenAccessible()
  397. {
  398. EntityAI ent = GetInventoryOwner();
  399. if (ent)
  400. return ent.AreChildrenAccessible();
  401. #ifdef DEVELOPER
  402. ErrorEx("no inventory owner found!");
  403. #endif
  404. return true; //just in case inventoy without owner exists somewhere (shouldn't!)
  405. }
  406. //! Returns true if the item is currently attached and outputs attachment slot id and name
  407. bool GetCurrentAttachmentSlotInfo(out int slot_id, out string slot_name)
  408. {
  409. slot_id = InventorySlots.INVALID;
  410. slot_name = "";
  411. InventoryLocation lcn = new InventoryLocation();
  412. GetCurrentInventoryLocation(lcn);
  413. if (lcn.GetType() == InventoryLocationType.ATTACHMENT)
  414. {
  415. slot_id = lcn.GetSlot();
  416. slot_name = InventorySlots.GetSlotName(slot_id);
  417. return true;
  418. }
  419. return false;
  420. }
  421. static void OnServerInventoryCommandStatic(ParamsReadContext ctx)
  422. {
  423. int tmp = -1;
  424. ctx.Read(tmp);
  425. int type = -1;
  426. if (!ctx.Read(type))
  427. return;
  428. switch (type)
  429. {
  430. case InventoryCommandType.SYNC_MOVE:
  431. {
  432. InventoryLocation src = new InventoryLocation();
  433. InventoryLocation dst = new InventoryLocation();
  434. src.ReadFromContext(ctx);
  435. dst.ReadFromContext(ctx);
  436. if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] t=" + GetGame().GetTime() + "ms ServerInventoryCommand cmd=" + typename.EnumToString(InventoryCommandType, type) + " src=" + InventoryLocation.DumpToStringNullSafe(src) + " dst=" + InventoryLocation.DumpToStringNullSafe(dst));
  437. if (!src.GetItem() || !dst.GetItem())
  438. {
  439. LogError("[syncinv] ServerInventoryCommand (cmd=SYNC_MOVE) dropped, item not in bubble");
  440. break; // not in bubble
  441. }
  442. LocationSyncMoveEntity(src, dst);
  443. break;
  444. }
  445. case InventoryCommandType.HAND_EVENT:
  446. {
  447. HandEventBase e = HandEventBase.CreateHandEventFromContext(ctx);
  448. if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] t=" + GetGame().GetTime() + "ms ServerInventoryCommand cmd=" + typename.EnumToString(InventoryCommandType, type) + " event=" + e.DumpToString());
  449. if (!e.GetSrcEntity())
  450. {
  451. Error("[syncinv] ServerInventoryCommand (cmd=HAND_EVENT) dropped, item not in bubble");
  452. break; // not in bubble
  453. }
  454. e.m_Player.GetHumanInventory().ProcessHandEvent(e);
  455. break;
  456. }
  457. case InventoryCommandType.FORCESWAP:
  458. case InventoryCommandType.SWAP:
  459. {
  460. InventoryLocation src1 = new InventoryLocation();
  461. InventoryLocation src2 = new InventoryLocation();
  462. InventoryLocation dst1 = new InventoryLocation();
  463. InventoryLocation dst2 = new InventoryLocation();
  464. src1.ReadFromContext(ctx);
  465. src2.ReadFromContext(ctx);
  466. dst1.ReadFromContext(ctx);
  467. dst2.ReadFromContext(ctx);
  468. if (src1.IsValid() && src2.IsValid() && dst1.IsValid() && dst2.IsValid())
  469. {
  470. if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] t=" + GetGame().GetTime() + "ms ServerInventoryCommand Swap src1=" + InventoryLocation.DumpToStringNullSafe(src1) + " src2=" + InventoryLocation.DumpToStringNullSafe(src2) + " dst1=" + InventoryLocation.DumpToStringNullSafe(dst1) + " dst2=" + InventoryLocation.DumpToStringNullSafe(dst2));
  471. LocationSwap(src1, src2, dst1, dst2);
  472. }
  473. else
  474. Error("ServerInventoryCommand - cannot swap, invalid location input: src1=" + InventoryLocation.DumpToStringNullSafe(src1) + " src2=" + InventoryLocation.DumpToStringNullSafe(src2) + " dst1=" + InventoryLocation.DumpToStringNullSafe(dst1) + " dst2=" + InventoryLocation.DumpToStringNullSafe(dst2));
  475. break;
  476. }
  477. }
  478. }
  479. /**
  480. * @fn LocationAddEntity
  481. * @brief adds item to inventory location
  482. * @return true if success, false otherwise
  483. **/
  484. static proto native bool LocationAddEntity(notnull InventoryLocation inv_loc);
  485. /**
  486. * @fn LocationRemoveEntity
  487. * @brief removes item from inventory location
  488. * @return true if success, false otherwise
  489. **/
  490. static proto native bool LocationRemoveEntity(notnull InventoryLocation inv_loc);
  491. /**
  492. * @fn LocationMoveEntity
  493. * @brief removes item from current inventory location and adds it to destination
  494. * @return true if success, false otherwise
  495. **/
  496. static proto native bool LocationMoveEntity(notnull InventoryLocation src_loc, notnull InventoryLocation dst_loc);
  497. /**
  498. * @fn LocationSyncMoveEntity
  499. * @brief synchronously removes item from current inventory location and adds it to destination
  500. * no anims involved
  501. * @return true if success, false otherwise
  502. **/
  503. static proto native bool LocationSyncMoveEntity(notnull InventoryLocation src_loc, notnull InventoryLocation dst_loc);
  504. /**
  505. * @fn LocationSwap
  506. * @brief swaps two entities
  507. * @return true if success, false otherwise
  508. **/
  509. static proto native bool LocationSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2);
  510. /**
  511. * @fn ServerLocationMoveEntity
  512. * @brief removes item from current inventory location and adds it to destination + sync via inventory command
  513. * @return true if success, false otherwise
  514. **/
  515. static proto native bool ServerLocationMoveEntity(notnull EntityAI item, ParamsWriteContext ctx);
  516. /**
  517. * @fn ServerLocationSyncMoveEntity
  518. * @brief synchronously removes item from current inventory location and adds it to destination + sync via
  519. * inventory command
  520. * no anims involved
  521. * @return true if success, false otherwise
  522. **/
  523. static proto native bool ServerLocationSyncMoveEntity(Man player, notnull EntityAI item, ParamsWriteContext ctx);
  524. /**
  525. * @fn ServerLocationSwap
  526. * @brief swaps two entities
  527. * @return true if success, false otherwise
  528. **/
  529. static proto native bool ServerLocationSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2, ParamsWriteContext ctx);
  530. /**
  531. * @fn ServerHandEvent
  532. * @brief hand event to clients
  533. * @return true if success, false otherwise
  534. **/
  535. static proto native bool ServerHandEvent(notnull Man player, notnull EntityAI item, ParamsWriteContext ctx);
  536. /**
  537. * @fn PrepareDropEntityPos
  538. * @brief Finds a transformation for the item to be dropped to
  539. * If the initial transforation overlaps with another conflicting entity (i.e. car) then the transform will be snapped to the nearest outer edge.
  540. * If no valid snapping transformation could be found, then the output from 'PlaceOnSurface' is used
  541. * @param useValuesInMatrix Is ignored if transformation overlaps with possible conflicting entity. If no valid transform is found and this is false then the transform at the owner is used for 'PlaceOnSurface'
  542. * @param conflictCheckDepth If -1 then no conflict depth check is performed, if 0 then it only checks to see if it conflicts but doesn't perform snapping
  543. * @return true if valid transformation found near owner
  544. **/
  545. static proto native bool PrepareDropEntityPos(EntityAI owner, notnull EntityAI item, out vector mat[4], bool useValuesInMatrix = false, int conflictCheckDepth = -1);
  546. static proto native bool TestDropEntityPos(EntityAI owner, notnull EntityAI item, out vector mat[4], bool useValuesInMatrix = false, int conflictCheckDepth = -1);
  547. /**@fn CanSwapEntities
  548. * @brief test if ordinary swap can be performed.
  549. *
  550. * Ordinary in the sense that the two items has equal sizes and can be swapped without
  551. * additional space.
  552. *
  553. * @param [in] item1 first item
  554. * @param [in] item2 second item
  555. * @return true if can be swapped
  556. */
  557. static proto native bool CanSwapEntities(notnull EntityAI item1, notnull EntityAI item2);
  558. static bool CanSwapEntitiesEx(notnull EntityAI item1, notnull EntityAI item2)
  559. {
  560. int slot;
  561. InventoryLocation il1 = new InventoryLocation();
  562. InventoryLocation il2 = new InventoryLocation();
  563. item2.GetInventory().GetCurrentInventoryLocation(il2);
  564. slot = il2.GetSlot();
  565. if (item1.CanBeSplit() && item1.GetQuantity() > item1.GetTargetQuantityMax(slot))
  566. return false;
  567. item1.GetInventory().GetCurrentInventoryLocation(il1);
  568. slot = il1.GetSlot();
  569. if (item2.CanBeSplit() && item2.GetQuantity() > item2.GetTargetQuantityMax(slot))
  570. return false;
  571. if (!item1.CanSwapEntities(item2, il2, il1) || !item2.CanSwapEntities(item1, il1, il2))
  572. {
  573. return false;
  574. }
  575. return CanSwapEntities(item1,item2);
  576. }
  577. /**@fn CanForceSwapEntities
  578. * @brief test if forced swap can be performed.
  579. *
  580. * @param [in] item1 is the forced item (primary)
  581. * @param [in] item1_dst optional destination for item1 (null if item1 goes to place of item2)
  582. * @param [in] item2 second item that will be replaced by the item1. (secondary)
  583. * @param [out] item2_dst second item's potential destination (if null, search for item_dst2 is ommited). if (NOT null AND IsValid()), then it is tried first, without search to inventory
  584. * @return true if can be force swapped
  585. */
  586. static proto native bool CanForceSwapEntities(notnull EntityAI item1, InventoryLocation item1_dst, notnull EntityAI item2, out InventoryLocation item2_dst);
  587. static bool CanForceSwapEntitiesEx(notnull EntityAI item1, InventoryLocation item1_dst, notnull EntityAI item2, out InventoryLocation item2_dst)
  588. {
  589. if (!CanForceSwapEntities(item1, item1_dst, item2, item2_dst) )
  590. return false;
  591. int slot;
  592. InventoryLocation il = new InventoryLocation;
  593. if (!item1.CanBeFSwaped())
  594. return false;
  595. if ( item1_dst == null)
  596. {
  597. item2.GetInventory().GetCurrentInventoryLocation(il);
  598. slot = il.GetSlot();
  599. }
  600. else
  601. {
  602. slot = item1_dst.GetSlot();
  603. }
  604. if ( item1.GetQuantity() > item1.GetTargetQuantityMax(slot) )
  605. return false;
  606. if ( item2_dst == null)
  607. {
  608. item1.GetInventory().GetCurrentInventoryLocation(il);
  609. slot = il.GetSlot();
  610. }
  611. else
  612. {
  613. slot = item2_dst.GetSlot();
  614. }
  615. if (!item1.CanSwapEntities(item2, item2_dst, item1_dst) || !item2.CanSwapEntities(item1, item1_dst, item2_dst))
  616. {
  617. return false;
  618. }
  619. if ( item2.GetQuantity() > item2.GetTargetQuantityMax(slot) )
  620. return false;
  621. return true;
  622. }
  623. proto native bool CanAddSwappedEntity(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2);
  624. ///@{ reservations
  625. const int c_InventoryReservationTimeoutMS = 5000;
  626. const int c_InventoryReservationTimeoutShortMS = 3000;
  627. static proto native bool AddInventoryReservation(EntityAI item, InventoryLocation dst, int timeout_ms);
  628. bool AddInventoryReservationEx(EntityAI item, InventoryLocation dst, int timeout_ms)
  629. {
  630. if (GetGame().IsMultiplayer() && GetGame().IsServer() )
  631. return true;
  632. bool ret_val = AddInventoryReservation(item, dst, timeout_ms);
  633. #ifdef ENABLE_LOGGING
  634. if ( LogManager.IsInventoryReservationLogEnable() )
  635. {
  636. DayZPlayer player = GetGame().GetPlayer();
  637. if ( player )
  638. {
  639. if (item)
  640. Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "AddInventoryReservation", player.ToString() );
  641. else
  642. Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "AddInventoryReservation", player.ToString() );
  643. }
  644. }
  645. #endif
  646. return ret_val;
  647. }
  648. static proto native bool ExtendInventoryReservation(EntityAI item, InventoryLocation dst, int timeout_ms);
  649. bool ExtendInventoryReservationEx(EntityAI item, InventoryLocation dst, int timeout_ms)
  650. {
  651. if (GetGame().IsMultiplayer() && GetGame().IsServer() )
  652. return true;
  653. bool ret_val = ExtendInventoryReservation(item,dst,timeout_ms);
  654. #ifdef ENABLE_LOGGING
  655. if ( LogManager.IsInventoryReservationLogEnable() )
  656. {
  657. DayZPlayer player = GetGame().GetPlayer();
  658. if ( player )
  659. {
  660. if (item)
  661. Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ExtendInventoryReservation", player.ToString() );
  662. else
  663. Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ExtendInventoryReservation", player.ToString() );
  664. }
  665. }
  666. #endif
  667. return ret_val;
  668. }
  669. static proto native bool ClearInventoryReservation(EntityAI item, InventoryLocation dst);
  670. bool ClearInventoryReservationEx(EntityAI item, InventoryLocation dst)
  671. {
  672. if (GetGame().IsMultiplayer() && GetGame().IsServer() )
  673. return true;
  674. bool ret_val = ClearInventoryReservation(item,dst);
  675. #ifdef ENABLE_LOGGING
  676. if ( LogManager.IsInventoryReservationLogEnable() )
  677. {
  678. DayZPlayer player = GetGame().GetPlayer();
  679. if ( player )
  680. {
  681. if (item)
  682. Debug.InventoryMoveLog("Reservation cleared result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ClearInventoryReservation", player.ToString() );
  683. else
  684. Debug.InventoryMoveLog("Reservation cleared result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ClearInventoryReservation", player.ToString() );
  685. }
  686. }
  687. #endif
  688. return ret_val;
  689. }
  690. //! Internally: HasInventoryReservationEx(item, dst, FindInventoryReservationMode.LEGACY, FindInventoryReservationMode.LEGACY)
  691. static proto native bool HasInventoryReservation(EntityAI item, InventoryLocation dst);
  692. //! Internally: !HasInventoryReservationEx(item, dst, FindInventoryReservationMode.ITEM, FindInventoryReservationMode.DST)
  693. static proto native bool HasInventoryReservationCanAdd(EntityAI item, InventoryLocation dst);
  694. //! itemMode will iterate over item reservations, parentMode will iterate over parent reservations
  695. static proto native bool HasInventoryReservationEx(EntityAI item, InventoryLocation dst, FindInventoryReservationMode itemMode, FindInventoryReservationMode parentMode);
  696. static proto native bool GetInventoryReservationCount(EntityAI item, InventoryLocation dst);
  697. proto native int GetAnyInventoryReservationCount();
  698. ///@} reservations
  699. ///@{ locks
  700. proto native bool CanLockInventoryWithKey(notnull EntityAI key);
  701. proto native bool CanUnlockInventoryWithKey(notnull EntityAI key);
  702. proto native void LockInventoryWithKey(notnull EntityAI key);
  703. proto native void UnlockInventoryWithKey(notnull EntityAI key);
  704. proto native bool HasKeys();
  705. proto native void LockInventory(int lockType);
  706. proto native void UnlockInventory(int lockType);
  707. proto native int GetScriptLockCount();
  708. proto native bool IsInventoryUnlocked();
  709. proto native bool IsInventoryLocked();
  710. proto native bool IsInventoryLockedForLockType(int lockType);
  711. proto native bool SetSlotLock(int slot, bool locked);
  712. proto native bool GetSlotLock(int slot);
  713. ///@} locks
  714. ///@{ anti-cheats
  715. const float c_MaxItemDistanceRadius = 2.5;
  716. static proto native bool CheckRequestSrc(notnull Man requestingPlayer, notnull InventoryLocation src, float radius);
  717. static proto native bool CheckDropRequest(notnull Man requestingPlayer, notnull InventoryLocation src, float radius);
  718. static proto native bool CheckTakeItemRequest(notnull Man requestingPlayer, notnull InventoryLocation src, notnull InventoryLocation dst, float radius);
  719. static proto native bool CheckMoveToDstRequest(notnull Man requestingPlayer, notnull InventoryLocation src, notnull InventoryLocation dst, float radius);
  720. static proto native bool CheckSwapItemsRequest(notnull Man requestingPlayer, notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2, float radius);
  721. static proto native bool CheckManipulatedObjectsDistances(notnull EntityAI e0, notnull EntityAI e1, float radius);
  722. ///@} anti-cheats
  723. ///@} Engine native functions
  724. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  725. ///@{ script functions
  726. /**@fn Init
  727. * @brief called by engine right after creation of entity
  728. **/
  729. void Init()
  730. {
  731. GetInventoryOwner().OnInventoryInit();
  732. }
  733. /// db load hooks
  734. bool OnStoreLoad(ParamsReadContext ctx, int version)
  735. {
  736. return true;
  737. }
  738. void OnAfterStoreLoad();
  739. /// db store hook
  740. void OnStoreSave(ParamsWriteContext ctx);
  741. void EEInit()
  742. {
  743. InventoryLocation src = new InventoryLocation;
  744. if (GetCurrentInventoryLocation(src))
  745. {
  746. if (src.GetType() == InventoryLocationType.HANDS)
  747. {
  748. Man man = Man.Cast(src.GetParent());
  749. if (man)
  750. {
  751. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("Inventory::EEInit - Man=" + man + " item=" + this);
  752. man.GetHumanInventory().OnEntityInHandsCreated(src);
  753. }
  754. }
  755. }
  756. }
  757. void EEDelete(EntityAI parent)
  758. {
  759. EntityAI item = GetInventoryOwner();
  760. Man player = item.GetHierarchyRootPlayer();
  761. if (player)
  762. player.GetInventory().ClearInventoryReservationEx(item, null);
  763. }
  764. /**@fn CreateInInventory
  765. * @brief creates entity somewhere in inventory
  766. *
  767. * @param[in] type \p item type to be placed in inventory
  768. * @return created entity or null
  769. **/
  770. EntityAI CreateInInventory(string type)
  771. {
  772. InventoryLocation loc = new InventoryLocation();
  773. if (FindFirstFreeLocationForNewEntity(type, FindInventoryLocationType.CARGO | FindInventoryLocationType.ATTACHMENT, loc))
  774. {
  775. switch (loc.GetType())
  776. {
  777. case InventoryLocationType.ATTACHMENT:
  778. return loc.GetParent().GetInventory().CreateAttachmentEx(type, loc.GetSlot());
  779. case InventoryLocationType.CARGO:
  780. return loc.GetParent().GetInventory().CreateEntityInCargoEx(type, loc.GetIdx(), loc.GetRow(), loc.GetCol(), loc.GetFlip());
  781. default:
  782. Error("CreateInInventory: unknown location for item");
  783. break;
  784. }
  785. }
  786. return null;
  787. }
  788. /**
  789. * @fn CanAddEntityToInventory
  790. * @brief ask inventory if item could be placed somewhere in inventory
  791. *
  792. * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are
  793. * traversed recursively from root to leafs.
  794. *
  795. * @param[in] item \p item to be placed in inventory
  796. * @return true if item can be added, false otherwise
  797. **/
  798. bool CanAddEntityToInventory(notnull EntityAI item, int flag = FindInventoryLocationType.ANY)
  799. {
  800. InventoryLocation il = new InventoryLocation();
  801. bool result = FindFreeLocationFor(item, flag, il);
  802. return result;
  803. }
  804. /**
  805. * @fn AddEntityToInventory
  806. * @brief add entity somewhere in inventory
  807. *
  808. * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are
  809. * traversed recursively from root to leafs.
  810. *
  811. * @NOTE: AddEntityToInventory does not perform the checks the CanAddEntityToInventory does
  812. * (script conditions for example).
  813. * if not sure, always call CanAddEntityToInventory before AddEntityToInventory.
  814. *
  815. * @param[in] item \p item to be placed in inventory
  816. * @return true if item can be added, false otherwise
  817. **/
  818. bool AddEntityToInventory(notnull EntityAI item)
  819. {
  820. InventoryLocation il = new InventoryLocation();
  821. bool result = FindFreeLocationFor(item, FindInventoryLocationType.ANY, il);
  822. if (result)
  823. return LocationAddEntity(il);
  824. return result;
  825. }
  826. /**
  827. * @brief Returns if entity can be removed from its current location
  828. **/
  829. bool CanRemoveEntity()
  830. {
  831. InventoryLocation il = new InventoryLocation;
  832. if (GetCurrentInventoryLocation(il))
  833. return LocationCanRemoveEntity(il);
  834. return false;
  835. }
  836. // Script version of CanAddEntity* methods based on InventoryLocation
  837. /**
  838. * @fn CanAddEntityInto
  839. * @brief Asks inventory if item could be placed in inventory, hands, etc.
  840. *
  841. * Location is based on the flags parameter. This method is used in methods that are
  842. * asking for if item can be placed in specific location, like CanAddEntityToInventory,
  843. * CanAddEntityInHands, etc.
  844. *
  845. * @param[in] item \p item to be placed in inventory
  846. * @param[in] flags \p FindInventoryLocationType
  847. * @return true if item can be added, false otherwise
  848. */
  849. bool CanAddEntityInto(notnull EntityAI item, FindInventoryLocationType flags = FindInventoryLocationType.ANY)
  850. {
  851. InventoryLocation loc = new InventoryLocation();
  852. return FindFreeLocationFor(item, flags, loc) && !item.IsHologram();
  853. }
  854. /**
  855. \brief Test if entity can be put to the inventory (Cargo, ProxyCargo, Attachment)
  856. */
  857. bool CanAddEntityIntoInventory(notnull EntityAI item)
  858. {
  859. return CanAddEntityInto(item, FindInventoryLocationType.ANY_CARGO | FindInventoryLocationType.ATTACHMENT);
  860. }
  861. /**
  862. \brief Test if entity can be put into hands
  863. */
  864. bool CanAddEntityIntoHands(notnull EntityAI item)
  865. {
  866. return CanAddEntityInto(item, FindInventoryLocationType.HANDS);
  867. }
  868. ///@{ synchronization
  869. bool OnInputUserDataProcess(ParamsReadContext ctx);
  870. bool OnInventoryJunctureFromServer(ParamsReadContext ctx);
  871. bool OnInventoryJunctureRepairFromServer(ParamsReadContext ctx);
  872. void OnInventoryJunctureFailureFromServer(ParamsReadContext ctx);
  873. void OnServerInventoryCommand(ParamsReadContext ctx);
  874. ///@} synchronization
  875. void OnInventoryFailure(InventoryCommandType type, InventoryValidationReason reason, InventoryLocation src, InventoryLocation dst);
  876. /**
  877. * @fn TakeEntityToInventory
  878. * @brief Put item anywhere into this entity (as attachment or into cargo, recursively)
  879. * @param[in] mode inventory mode
  880. * @param[in] flags preferred location, @see FindInventoryLocationType
  881. * @param[in] item item to be taken in inventory
  882. * @return true on success
  883. *
  884. * @NOTE: Predictive.* / Local.* functions:
  885. * a) 'Predictive' means that the operation uses Client-Side Prediction, i.e. the
  886. * action runs the same code on both, client AND server.
  887. *
  888. * b) 'Local' operation executes from where it is run, i.e. the operation from client
  889. * is NOT sent to server, and only client performs the inventory operation (TakeEntityTo.*)
  890. * this behaviour is necessary when inventory operations are ALREADY synchronized by another
  891. * means, like Actions.
  892. * For example @see ActionTakeItemToHands that performs OnCompleteServer
  893. * and OnCompleteClient with synchronization set to false in order to avoid duplicate
  894. * synchronization.
  895. **/
  896. bool TakeEntityToInventory(InventoryMode mode, FindInventoryLocationType flags, notnull EntityAI item)
  897. {
  898. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  899. InventoryLocation src = new InventoryLocation();
  900. if (item.GetInventory().GetCurrentInventoryLocation(src))
  901. {
  902. InventoryLocation dst = new InventoryLocation();
  903. if (FindFreeLocationFor(item, flags, dst))
  904. return TakeToDst(mode, src, dst);
  905. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Warning - no room for item");
  906. return false;
  907. }
  908. Error("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Error - src has no inventory location");
  909. return false;
  910. }
  911. /// helper that finds location first, then moves the entity into it
  912. bool TakeEntityToTargetInventory(InventoryMode mode, notnull EntityAI target, FindInventoryLocationType flags, notnull EntityAI item)
  913. {
  914. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  915. InventoryLocation src = new InventoryLocation();
  916. if (item.GetInventory().GetCurrentInventoryLocation(src))
  917. {
  918. InventoryLocation dst = new InventoryLocation();
  919. if (target.GetInventory().FindFreeLocationFor(item, flags, dst))
  920. return TakeToDst(mode, src, dst);
  921. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") target=" + target + " item=" + item + " Warning - no room for item in target");
  922. return false;
  923. }
  924. Error("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") target=" + target + " item=" + item + " Error - src has no inventory location");
  925. return false;
  926. }
  927. /**@fn TakeToDst
  928. * @brief move src to dst
  929. * @param[in] mode inventory mode
  930. * @param[in] src source location of the item
  931. * @param[in] dst destination location of the item
  932. **/
  933. bool TakeToDst (InventoryMode mode, notnull InventoryLocation src, notnull InventoryLocation dst)
  934. {
  935. bool ret;
  936. switch (mode)
  937. {
  938. case InventoryMode.SERVER:
  939. ret = LocationSyncMoveEntity(src, dst);
  940. if (ret && dst.IsValid())
  941. InventoryInputUserData.SendServerMove(null, InventoryCommandType.SYNC_MOVE, src, dst);
  942. return ret;
  943. case InventoryMode.LOCAL:
  944. ret = LocationSyncMoveEntity(src, dst);
  945. return ret;
  946. default:
  947. return false;
  948. }
  949. return false;
  950. }
  951. /**@fn TakeEntityToCargo
  952. * @brief moves item to cargo of this intentory
  953. * @param[in] mode inventory mode
  954. * @param[in] item item to be put in this inventory as cargo
  955. **/
  956. bool TakeEntityToCargo(InventoryMode mode, notnull EntityAI item)
  957. {
  958. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  959. return TakeEntityToInventory(mode, FindInventoryLocationType.CARGO, item);
  960. }
  961. /// Put item into into cargo of another entity
  962. bool TakeEntityToTargetCargo(InventoryMode mode, notnull EntityAI target, notnull EntityAI item)
  963. {
  964. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetCgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + target);
  965. return TakeEntityToTargetInventory(mode, target, FindInventoryLocationType.CARGO, item);
  966. }
  967. /**@fn TakeEntityToCargoEx
  968. * @brief moves item on specific cargo location
  969. * @param[in] mode inventory mode
  970. * @param[in] item item to be put in this inventory as cargo
  971. **/
  972. bool TakeEntityToCargoEx(InventoryMode mode, notnull EntityAI item, int idx, int row, int col)
  973. {
  974. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " row=" + row + " col=" + col);
  975. InventoryLocation src = new InventoryLocation();
  976. if (item.GetInventory().GetCurrentInventoryLocation(src))
  977. {
  978. InventoryLocation dst = new InventoryLocation();
  979. dst.SetCargo(GetInventoryOwner(), item, idx, row, col, item.GetInventory().GetFlipCargo());
  980. return TakeToDst(mode, src, dst);
  981. }
  982. Error("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " row=" + row + " col=" + col + " Error - src has no inventory location");
  983. return false;
  984. }
  985. /// Put item into into cargo on specific cargo location of another entity
  986. bool TakeEntityToTargetCargoEx(InventoryMode mode, notnull CargoBase cargo, notnull EntityAI item, int row, int col)
  987. {
  988. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetCgoEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + cargo.GetCargoOwner() + " row=" + row + " col=" + col);
  989. InventoryLocation src = new InventoryLocation();
  990. if (item.GetInventory().GetCurrentInventoryLocation(src))
  991. {
  992. InventoryLocation dst = new InventoryLocation();
  993. dst.SetCargoAuto(cargo, item, row, col, item.GetInventory().GetFlipCargo());
  994. return TakeToDst(mode, src, dst);
  995. }
  996. Error("[inv] I::Take2TargetCgoEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + cargo.GetCargoOwner() + " row=" + row + " col=" + col);
  997. return false;
  998. }
  999. bool TakeEntityAsAttachmentEx(InventoryMode mode, notnull EntityAI item, int slot)
  1000. {
  1001. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " slot=" + slot);
  1002. return TakeEntityAsTargetAttachmentEx(mode, GetInventoryOwner(), item, slot);
  1003. }
  1004. /// put item as attachment of target
  1005. bool TakeEntityAsTargetAttachmentEx(InventoryMode mode, notnull EntityAI target, notnull EntityAI item, int slot)
  1006. {
  1007. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetAttEx(" + typename.EnumToString(InventoryMode, mode) + ") as ATT of target=" + target + " item=" + item + " slot=" + slot);
  1008. InventoryLocation src = new InventoryLocation();
  1009. if (item.GetInventory().GetCurrentInventoryLocation(src))
  1010. {
  1011. EntityAI att = target.GetInventory().FindAttachment(slot);
  1012. if (att)
  1013. {
  1014. if (mode == InventoryMode.SERVER)
  1015. {
  1016. att.CombineItemsEx(item, true);
  1017. }
  1018. else
  1019. {
  1020. att.CombineItemsClient(item, true);
  1021. }
  1022. return true;
  1023. }
  1024. else if (item.CanBeSplit() && item.GetTargetQuantityMax(slot) < item.GetQuantity())
  1025. {
  1026. if (mode == InventoryMode.SERVER)
  1027. {
  1028. item.SplitIntoStackMaxEx(target, slot);
  1029. }
  1030. else
  1031. {
  1032. item.SplitIntoStackMaxClient(target,slot);
  1033. }
  1034. return true;
  1035. }
  1036. else
  1037. {
  1038. InventoryLocation dst = new InventoryLocation();
  1039. dst.SetAttachment(target, item, slot);
  1040. return TakeToDst(mode, src, dst);
  1041. }
  1042. }
  1043. Error("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Error - src has no inventory location");
  1044. return false;
  1045. }
  1046. bool TakeEntityAsAttachment(InventoryMode mode, notnull EntityAI item)
  1047. {
  1048. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Att(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  1049. return TakeEntityToInventory(mode, FindInventoryLocationType.ATTACHMENT, item);
  1050. }
  1051. bool TakeEntityAsTargetAttachment(InventoryMode mode, notnull EntityAI target, notnull EntityAI item)
  1052. {
  1053. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  1054. return TakeEntityToTargetInventory(mode, target, FindInventoryLocationType.ATTACHMENT, item);
  1055. }
  1056. /// helper function for swap
  1057. static bool MakeDstForSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, out InventoryLocation dst1, out InventoryLocation dst2)
  1058. {
  1059. if (dst1 == null)
  1060. dst1 = new InventoryLocation();
  1061. dst1.Copy(src1);
  1062. dst1.CopyLocationFrom(src2, false);
  1063. dst1.SetFlip(src1.GetItem().GetInventory().GetFlipCargo()); // update flip flag from inventory item
  1064. if (dst2 == null)
  1065. dst2 = new InventoryLocation();
  1066. dst2.Copy(src2);
  1067. dst2.CopyLocationFrom(src1, false);
  1068. dst2.SetFlip(src2.GetItem().GetInventory().GetFlipCargo()); // update flip flag from inventory item
  1069. return true;
  1070. }
  1071. /// helper function for swap
  1072. static bool MakeSrcAndDstForSwap(notnull EntityAI item1, notnull EntityAI item2, out InventoryLocation src1, out InventoryLocation src2, out InventoryLocation dst1, out InventoryLocation dst2)
  1073. {
  1074. if (src1 == null)
  1075. src1 = new InventoryLocation();
  1076. if (src2 == null)
  1077. src2 = new InventoryLocation();
  1078. if (item1.GetInventory().GetCurrentInventoryLocation(src1) && item2.GetInventory().GetCurrentInventoryLocation(src2))
  1079. return MakeDstForSwap(src1, src2, dst1, dst2);
  1080. return false;
  1081. }
  1082. /// helper function for ForceSwap
  1083. static bool MakeSrcAndDstForForceSwap(notnull EntityAI item1, notnull EntityAI item2, out InventoryLocation src1, out InventoryLocation src2, out InventoryLocation dst1, notnull InventoryLocation dst2)
  1084. {
  1085. if (src1 == null)
  1086. src1 = new InventoryLocation();
  1087. if (src2 == null)
  1088. src2 = new InventoryLocation();
  1089. if (item1.GetInventory().GetCurrentInventoryLocation(src1) && item2.GetInventory().GetCurrentInventoryLocation(src2))
  1090. {
  1091. // src1 -> dst_of(src2)
  1092. if (dst1 == null)
  1093. dst1 = new InventoryLocation();
  1094. dst1.Copy(src1);
  1095. dst1.CopyLocationFrom(src2, false);
  1096. dst1.SetFlip(dst1.GetItem().GetInventory().GetFlipCargo());
  1097. // src2 -> dst2 from user
  1098. return true;
  1099. }
  1100. return false;
  1101. }
  1102. bool SwapEntities(InventoryMode mode, notnull EntityAI item1, notnull EntityAI item2)
  1103. {
  1104. return false;
  1105. }
  1106. bool ForceSwapEntities(InventoryMode mode, notnull EntityAI item1, notnull EntityAI item2, notnull InventoryLocation item2_dst)
  1107. {
  1108. return false;
  1109. }
  1110. static bool SetGroundPosByOwner(EntityAI owner, notnull EntityAI item, out InventoryLocation ground)
  1111. {
  1112. vector m4[4];
  1113. Math3D.MatrixIdentity4(m4);
  1114. bool success = GameInventory.PrepareDropEntityPos(owner, item, m4, false, GameConstants.INVENTORY_ENTITY_DROP_OVERLAP_DEPTH);
  1115. ground.SetGround(item, m4);
  1116. return success;
  1117. }
  1118. bool DropEntity(InventoryMode mode, EntityAI owner, notnull EntityAI item)
  1119. {
  1120. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  1121. InventoryLocation src = new InventoryLocation();
  1122. if (item.GetInventory().GetCurrentInventoryLocation(src))
  1123. {
  1124. InventoryLocation dst = new InventoryLocation();
  1125. if (!SetGroundPosByOwner(owner, item, dst))
  1126. {
  1127. OnInventoryFailure(InventoryCommandType.SYNC_MOVE, InventoryValidationReason.DROP_PREVENTED, src, dst);
  1128. return false;
  1129. }
  1130. return TakeToDst(mode, src, dst);
  1131. }
  1132. Error("DropEntity - No inventory location");
  1133. return false;
  1134. }
  1135. static bool SetGroundPosByTransform(EntityAI owner, notnull EntityAI item, out InventoryLocation ground, vector transform[4])
  1136. {
  1137. bool success = GameInventory.PrepareDropEntityPos(owner, item, transform, true, GameConstants.INVENTORY_ENTITY_DROP_OVERLAP_DEPTH);
  1138. ground.SetGround(item, transform);
  1139. return success;
  1140. }
  1141. bool DropEntityWithTransform(InventoryMode mode, EntityAI owner, notnull EntityAI item, vector transform[4])
  1142. {
  1143. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  1144. InventoryLocation src = new InventoryLocation();
  1145. if (item.GetInventory().GetCurrentInventoryLocation(src))
  1146. {
  1147. InventoryLocation dst = new InventoryLocation();
  1148. SetGroundPosByTransform(owner, item, dst, transform);
  1149. return TakeToDst(mode, src, dst);
  1150. }
  1151. Error("DropEntityWithTransform - No inventory location");
  1152. return false;
  1153. }
  1154. static void SetGroundPosByOwnerBounds(EntityAI owner, notnull EntityAI item, out InventoryLocation ground, vector halfExtents, float angle, float cosAngle, float sinAngle)
  1155. {
  1156. vector m4[4];
  1157. owner.GetTransform(m4);
  1158. vector randomPos = Vector(Math.RandomFloat(-halfExtents[0], halfExtents[0]), 0, Math.RandomFloat(-halfExtents[2], halfExtents[2]));
  1159. randomPos = vector.RotateAroundZero(randomPos, vector.Up, cosAngle, sinAngle);
  1160. float dist = randomPos[0] * m4[1][0] + randomPos[1] * m4[1][1] + randomPos[2] * m4[1][2];
  1161. m4[3][0] = m4[3][0] + randomPos[0];
  1162. m4[3][1] = m4[3][1] - dist + halfExtents[1];
  1163. m4[3][2] = m4[3][2] + randomPos[2];
  1164. item.PlaceOnSurfaceRotated(m4, Vector(m4[3][0], m4[3][1], m4[3][2]));
  1165. ground.SetGround(item, m4);
  1166. }
  1167. bool DropEntityInBounds(InventoryMode mode, EntityAI owner, notnull EntityAI item, vector halfExtents, float angle, float cosAngle, float sinAngle)
  1168. {
  1169. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item);
  1170. InventoryLocation src = new InventoryLocation();
  1171. if (item.GetInventory().GetCurrentInventoryLocation(src))
  1172. {
  1173. InventoryLocation dst = new InventoryLocation();
  1174. SetGroundPosByOwnerBounds(owner, item, dst, halfExtents, angle, cosAngle, sinAngle);
  1175. return TakeToDst(mode, src, dst);
  1176. }
  1177. Error("DropEntityInBounds - No inventory location");
  1178. return false;
  1179. }
  1180. bool LocalDestroyEntity(notnull EntityAI item)
  1181. {
  1182. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::LocalDestroyEntity inv item=" + item);
  1183. InventoryLocation src = new InventoryLocation();
  1184. if (item.GetInventory().GetCurrentInventoryLocation(src))
  1185. {
  1186. if (src.GetType() == InventoryLocationType.HANDS)
  1187. Error("[inv] Source location == HANDS, player has to handle this");
  1188. GetGame().ObjectDelete(src.GetItem());
  1189. return true;
  1190. }
  1191. Error("LocalDestroyEntity: No inventory location");
  1192. return false;
  1193. }
  1194. bool ReplaceItemWithNew(InventoryMode mode, ReplaceItemWithNewLambdaBase lambda)
  1195. {
  1196. InventoryLocation src = new InventoryLocation();
  1197. if (lambda.m_OldItem.GetInventory().GetCurrentInventoryLocation(src))
  1198. {
  1199. if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::ReplaceItemWithNew executing lambda=" + lambda + "on old_item=" + lambda.m_OldItem);
  1200. lambda.Execute();
  1201. return true;
  1202. }
  1203. Error("[inv] I::ReplaceItemWithNew - no inventory location");
  1204. return false;
  1205. }
  1206. proto native bool GetFlipCargo();
  1207. proto native void SetFlipCargo(bool flip);
  1208. proto native void FlipCargo();
  1209. proto native void ResetFlipCargo();
  1210. ///@} script functions
  1211. }