//------------------------------------------------------- enum InventoryCommandType { MOVE, ///< generic move, may involve animations SYNC_MOVE, ///< synchronous move. action is finished immeadiately, no animations involved HAND_EVENT, ///< event for hands SWAP, ///< swap two entities (simple swap of compatible objects) FORCESWAP, ///< Forced swap two entities. First goes to second's place, second goes "somewhere else" DESTROY, ///< destroy of entity right in inventory REPLACE, ///< replace of entity in inventory (@NOTE: hands goes through HAND_EVENT) USER_RESERVATION_CANCEL ///< Clear user reserved inventory space }; enum InventoryJunctureType { TAKE, ///< taking from ground SWAP, ///< swapping from ground //LOAD, ///< load mag from ground }; //! NOTE: PREDICTIVE is not to be used at all in multiplayer enum InventoryMode { PREDICTIVE, ///< 'Predictive' means that the operation uses Client-Side Prediction, i.e. the action runs the same code on both, client AND server LOCAL, ///< 'Local' operation executes from where it is run JUNCTURE, ///< 'Juncture' operation is used whenever there is possibility of race condition, i.e. two players picking same item from ground SERVER, ///< 'Server' mode operation is required if and only if the operation runs only on server (creates and/or destroys objects) }; enum InventoryValidationResult { FAILED, JUNCTURE, SUCCESS }; enum InventoryValidationReason { UNKNOWN, JUNCTURE_DENIED, DROP_PREVENTED }; class InventoryValidation { bool m_IsJuncture = false; bool m_IsRemote = false; InventoryValidationResult m_Result = InventoryValidationResult.FAILED; InventoryValidationReason m_Reason = InventoryValidationReason.UNKNOWN; bool IsAuthoritative() { return !m_IsJuncture && !m_IsRemote; } }; enum InventoryCheckContext { DEFAULT, SYNC_CHECK, } enum FindInventoryReservationMode { //! The original logic, finds anything depending on the parameters, item or dst required LEGACY, //! Find a reservation for the item EXCLUDING the dst, item and dst required ITEM, //! Find a reservation for the dst EXCLUDING the item, item and dst required DST, //! Find an exact reservation for item and dst, item and dst required EQUAL, }; /**@class GameInventory * @brief script counterpart to engine's class Inventory **/ class GameInventory { protected static int m_inventory_check_context = InventoryCheckContext.DEFAULT; //------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///@{ Engine native functions /** * @fn GetInventoryOwner * @return entity associated with this inventory **/ proto native EntityAI GetInventoryOwner(); #ifdef DEVELOPER /** * @fn DumpInventoryDebug * @brief dump out failed inventory operations of the current inventory **/ proto native void DumpInventoryDebug(); /** * @fn DumpStaticInventoryDebug * @brief dump out a more global list of failed inventory operations **/ static proto native void DumpStaticInventoryDebug(); #endif /** * @fn HasEntityInInventory * @brief query inventory if item is somewhere * * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are * traversed recursively from root to leafs. * * @param[in] item \p item to be found in inventory * @return true if found, false otherwise **/ proto native bool HasEntityInInventory(notnull EntityAI item); /** * @fn EnumerateInventory * @brief enumerate inventory using traversal type and filling items array * @param[in] tt used traversal type * @param[out] items items in inventory (in order determined by traversal) * @return true if found any, false otherwise **/ proto native bool EnumerateInventory(InventoryTraversalType tt, out array items); /** * @fn CountInventory * @brief almost identical to EnumerateInventory except it does not return items * @return number of items **/ proto native int CountInventory(); ///@{ cargo proto native CargoBase GetCargo(); proto native CargoBase GetCargoFromIndex(int index); /** * @brief Create Entity of specified type in cargo of entity **/ proto native EntityAI CreateEntityInCargo(string typeName); /** * @brief Create Entity of specified type in cargo of entity at coordinates (row, col) * @param typeName type to create * @param row the row of cargo to be created at * @param col the column of cargo to be created at **/ proto native EntityAI CreateEntityInCargoEx(string typeName, int idx, int row, int col, bool flip); proto native bool HasEntityInCargo(notnull EntityAI e); proto native bool HasEntityInCargoEx(notnull EntityAI e, int idx, int row, int col); proto native bool CanAddEntityInCargo(notnull EntityAI e, bool flip); proto native bool CanAddEntityInCargoEx(notnull EntityAI e, int idx, int row, int col, bool flip); proto native bool CanAddEntityInCargoExLoc(InventoryLocation loc); 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); 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); //proto native bool AddEntityInCargo (notnull EntityAI owner, EntityAI cargo); //proto native bool AddEntityInCargoEx (notnull EntityAI owner, notnull EntityAI e, int idx, int row, int col); proto native bool CanRemoveEntityInCargo(notnull EntityAI e); proto native bool CanRemoveEntityInCargoEx(notnull EntityAI e, int idx, int row, int col); ///@} cargo ///@{ attachments /**@fn GetSlotId * @param index index into array of configured slots (@see GetSlotIdCount) * @return slot where this item belongs **/ proto native int GetSlotId(int index); /**@fn GetSlotIdCount * @return number of slots this item can belong to **/ proto native int GetSlotIdCount(); /**@fn GetAttachmentSlotId * @param index index of the slot for attachment (@see GetAttachmentSlotsCount) * @return slot for attachment **/ proto native int GetAttachmentSlotId(int index); /**@fn GetAttachmentSlotsCount * @return number of slots for attachments **/ proto native int GetAttachmentSlotsCount(); /**@fn HasAttachmentSlot * @return true if this entity has attachments slot with id=slotId **/ /*proto native*/bool HasAttachmentSlot(int slotId) //TODO - flip to code { int count = GetAttachmentSlotsCount(); for (int i = 0; i < count; i++) { if (GetAttachmentSlotId(i) == slotId) return true; } return false; } /**@fn HasInventorySlot * @return true if this entity has inventory slot with id=slotId **/ proto native bool HasInventorySlot(int slotId); /**@fn AttachmentCount * @brief Returns count of attachments attached to this item **/ proto native int AttachmentCount(); /**@fn CreateAttachment * @brief Create Entity of specified type as attachment of entity **/ proto native EntityAI CreateAttachment(string typeName); /**@fn CreateAttachmentEx * @brief Create Entity of specified type as attachment of entity * @param typeName type to create * @param slotId the slot to be created in **/ proto native EntityAI CreateAttachmentEx(string typeName, int slotId); /**@fn GetAttachmentFromIndex * @return attached entity by attachment index * @NOTE: index is not slot! for getting attachment by slot use FindAttachment **/ proto native EntityAI GetAttachmentFromIndex(int index); /** * @brief Returns attached entity in slot (you can use InventorySlots.GetSlotIdFromString(name) to get slot id) **/ proto native EntityAI FindAttachment(int slot); /** * @brief Returns attached entity in slot (you can use EntityAI.GetActionComponentName to get slot id) **/ proto native EntityAI FindAttachmentByName(string slotName); /**@fn HasAttachment * @brief brief Returns True if entity is attached to this **/ proto native bool HasAttachment(notnull EntityAI e); /**@fn HasAttachmentEx * @brief brief Returns True if entity is attached to this in slot **/ proto native bool HasAttachmentEx(notnull EntityAI e, int slot); /**@fn CanAddAttachment * @brief Check if attachment can be added to any slot * @return true if entity can be added as attachment **/ proto native bool CanAddAttachment(notnull EntityAI e); /**@fn CanAddAttachmentEx * @brief Check if attachment can be added to slot * @NOTE: Note that slot index IS NOT slot ID! Slot ID is defined in DZ/data/config.cpp **/ proto native bool CanAddAttachmentEx(notnull EntityAI e, int slot); proto native bool CanRemoveAttachment(EntityAI attachment); proto native bool CanRemoveAttachmentEx(EntityAI attachment, int slot); //proto native bool RemoveAttachment(EntityAI attachment); //proto native bool RemoveAttachmentEx(EntityAI attachment, int slot); /** * @brief Returns placeholder entity for slot (naked arms, legs etc) **/ proto native EntityAI FindPlaceholderForSlot(int slot); proto native bool IsPlaceholderEntity(notnull Object e); ///@} attachments /** * @fn GetCurrentInventoryLocation * @brief returns information about current item location * @param[out] loc \p current InventoryLocation * @return true if current location retrieved (and is valid) **/ proto native bool GetCurrentInventoryLocation(out notnull InventoryLocation loc); /** * @brief FindFreeLocationFor * @param[in] item item that would be placed in inventory * @param[out] loc the InventoryLocation the item can be placed to * @return true if found any, false otherwise **/ proto native bool FindFreeLocationFor(notnull EntityAI item, FindInventoryLocationType flags, out notnull InventoryLocation loc); /** * @brief FindFreeLocationForEx * @param[in] item item that would be placed in inventory * @param[out] loc the InventoryLocation the item can be placed to * @return true if found any, false otherwise **/ proto native bool FindFreeLocationForEx(notnull EntityAI item, FindInventoryLocationType flags, notnull InventoryLocation exclude, out notnull InventoryLocation loc); /** * @brief FindFirstFreeLocationForNewEntity * @param[in] item_type item type that would be placed in inventory * @param[out] loc the InventoryLocation the item can be placed to * @return true if found any, false otherwise **/ proto native bool FindFirstFreeLocationForNewEntity(string item_type, FindInventoryLocationType flags, out notnull InventoryLocation loc); /**@fn FindFreeLocationsFor * @brief searches inventory for suitable location for @item * @param[in] item item that would be placed in inventory * @param[in] flags flags affecting selection ( @see FindInventoryLocation ) * @param[out] locs array of InventoryLocations the item can be placed to * @return number of suitable inventory locations * * Example: * @code * Entity bino = GetGame().CreateObject("Binoculars", "3312 0 854"); * EntityAI bino2; * if (Class.CastTo(bino2, bino)) * { * array a = new array; // 1) allocate memory for array * for (int i = 0; i < 10; ++i) * { * a.Insert(new InventoryLocation); // 2) pre-allocate inventorylocations from script. engine only fills them in, engine does not allocate memory to avoid mixing. * } * * int sz = player.FindFreeLocationsFor(bino, FindInventoryLocationType.ANY_CARGO | FindInventoryLocationType.ATTACHMENT, a); // 3) ask engine to fill the locations * if (sz > 0) * { * InventoryLocation src = new InventoryLocation; * bino2.GetCurrentInventoryLocation(src); // 5) get current location * * InventoryLocation dst = a[0]; // 6) get first (this is only example) * * ... use src and dst * } * } * @endcode **/ proto native int FindFreeLocationsFor(notnull EntityAI item, FindInventoryLocationType flags, out notnull array locs); /** * @fn LocationCreateEntity * @brief creates new item directly at location * @param[in] type \p item type to be placed in inventory * @return created entity, null otherwise **/ static proto native EntityAI LocationCreateEntity(notnull InventoryLocation inv_loc, string type, int iSetupFlags, int iRotation); /** * @fn LocationCreateLocalEntity * @brief creates new local item directly at location * @NOTE: the item is created localy, i.e. it's not registered to network * @param[in] type \p item type to be placed in inventory * @return created entity, null otherwise **/ static proto native EntityAI LocationCreateLocalEntity(notnull InventoryLocation inv_loc, string type, int iSetupFlags, int iRotation); /** * @fn LocationCanAddEntity * @brief queries if the entity contained in inv_loc.m_item can be added to ground/attachment/cargo/hands/... * @return true if can be added, false otherwise **/ static proto native bool LocationCanAddEntity(notnull InventoryLocation inv_loc); //Added script check to LocationCanAddEntity static bool LocationCanAddEntityEx(notnull InventoryLocation inv_loc) { return LocationCanAddEntity(inv_loc); } /** * @fn LocationTestAddEntity * @brief test if the entity contained in inv_loc.m_item can be added to ground/attachment/cargo/hands/... * @return true if can be added, false otherwise **/ 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); /** * @fn LocationCanRemoveEntity * @brief queries if the entity contained in inv_loc.m_item can be removed from ground/attachment/cargo/hands/... * @return true if can be added, false otherwise **/ static proto native bool LocationCanRemoveEntity(notnull InventoryLocation inv_loc); /** * @fn LocationCanMoveEntity * @brief queries if the entity contained in inv_loc.m_item can be moved to another location * This is a shorthand for CanRemove + CanAdd query * @return true if can be moved, false otherwise **/ static proto native bool LocationCanMoveEntity(notnull InventoryLocation src, notnull InventoryLocation dst); static int GetInventoryCheckContext() { return m_inventory_check_context; } static bool LocationCanMoveEntitySyncCheck(notnull InventoryLocation src, notnull InventoryLocation dst) { m_inventory_check_context = InventoryCheckContext.SYNC_CHECK; bool result = LocationCanMoveEntity(src, dst); m_inventory_check_context = InventoryCheckContext.DEFAULT; return result; } /** * @fn LocationCanAddEntity * @brief query the entity at the specific inventory location * @return entity at the location, null otherwise **/ static proto native EntityAI LocationGetEntity(notnull InventoryLocation inv_loc); //! Returns true if this Inventory owner is in cargo of something bool IsInCargo() { InventoryLocation lcn = new InventoryLocation(); GetCurrentInventoryLocation(lcn); if (lcn.GetType() == InventoryLocationType.CARGO) { return true; } return false; } //! Returns true if this Inventory owner is an attachment of something bool IsAttachment() { InventoryLocation lcn = new InventoryLocation(); GetCurrentInventoryLocation(lcn); if (lcn.GetType() == InventoryLocationType.ATTACHMENT) { return true; } return false; } //! Returns true if inventory owner or his hiearchy parents are in cargo bool IsCargoInHiearchy() { InventoryLocation lcn = new InventoryLocation(); EntityAI ent = GetInventoryOwner(); while (ent) { if (ent.GetInventory().GetCurrentInventoryLocation(lcn) && lcn.IsValid()) { if (lcn.GetType() == InventoryLocationType.CARGO || lcn.GetType() == InventoryLocationType.PROXYCARGO) return true; } ent = ent.GetHierarchyParent(); } return false; } //! Returns true if item is considered reachable within inventory bool AreChildrenAccessible() { EntityAI ent = GetInventoryOwner(); if (ent) return ent.AreChildrenAccessible(); #ifdef DEVELOPER ErrorEx("no inventory owner found!"); #endif return true; //just in case inventoy without owner exists somewhere (shouldn't!) } //! Returns true if the item is currently attached and outputs attachment slot id and name bool GetCurrentAttachmentSlotInfo(out int slot_id, out string slot_name) { slot_id = InventorySlots.INVALID; slot_name = ""; InventoryLocation lcn = new InventoryLocation(); GetCurrentInventoryLocation(lcn); if (lcn.GetType() == InventoryLocationType.ATTACHMENT) { slot_id = lcn.GetSlot(); slot_name = InventorySlots.GetSlotName(slot_id); return true; } return false; } static void OnServerInventoryCommandStatic(ParamsReadContext ctx) { int tmp = -1; ctx.Read(tmp); int type = -1; if (!ctx.Read(type)) return; switch (type) { case InventoryCommandType.SYNC_MOVE: { InventoryLocation src = new InventoryLocation(); InventoryLocation dst = new InventoryLocation(); src.ReadFromContext(ctx); dst.ReadFromContext(ctx); if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] t=" + GetGame().GetTime() + "ms ServerInventoryCommand cmd=" + typename.EnumToString(InventoryCommandType, type) + " src=" + InventoryLocation.DumpToStringNullSafe(src) + " dst=" + InventoryLocation.DumpToStringNullSafe(dst)); if (!src.GetItem() || !dst.GetItem()) { LogError("[syncinv] ServerInventoryCommand (cmd=SYNC_MOVE) dropped, item not in bubble"); break; // not in bubble } LocationSyncMoveEntity(src, dst); break; } case InventoryCommandType.HAND_EVENT: { HandEventBase e = HandEventBase.CreateHandEventFromContext(ctx); if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] t=" + GetGame().GetTime() + "ms ServerInventoryCommand cmd=" + typename.EnumToString(InventoryCommandType, type) + " event=" + e.DumpToString()); if (!e.GetSrcEntity()) { Error("[syncinv] ServerInventoryCommand (cmd=HAND_EVENT) dropped, item not in bubble"); break; // not in bubble } e.m_Player.GetHumanInventory().ProcessHandEvent(e); break; } case InventoryCommandType.FORCESWAP: case InventoryCommandType.SWAP: { InventoryLocation src1 = new InventoryLocation(); InventoryLocation src2 = new InventoryLocation(); InventoryLocation dst1 = new InventoryLocation(); InventoryLocation dst2 = new InventoryLocation(); src1.ReadFromContext(ctx); src2.ReadFromContext(ctx); dst1.ReadFromContext(ctx); dst2.ReadFromContext(ctx); if (src1.IsValid() && src2.IsValid() && dst1.IsValid() && dst2.IsValid()) { 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)); LocationSwap(src1, src2, dst1, dst2); } else Error("ServerInventoryCommand - cannot swap, invalid location input: src1=" + InventoryLocation.DumpToStringNullSafe(src1) + " src2=" + InventoryLocation.DumpToStringNullSafe(src2) + " dst1=" + InventoryLocation.DumpToStringNullSafe(dst1) + " dst2=" + InventoryLocation.DumpToStringNullSafe(dst2)); break; } } } /** * @fn LocationAddEntity * @brief adds item to inventory location * @return true if success, false otherwise **/ static proto native bool LocationAddEntity(notnull InventoryLocation inv_loc); /** * @fn LocationRemoveEntity * @brief removes item from inventory location * @return true if success, false otherwise **/ static proto native bool LocationRemoveEntity(notnull InventoryLocation inv_loc); /** * @fn LocationMoveEntity * @brief removes item from current inventory location and adds it to destination * @return true if success, false otherwise **/ static proto native bool LocationMoveEntity(notnull InventoryLocation src_loc, notnull InventoryLocation dst_loc); /** * @fn LocationSyncMoveEntity * @brief synchronously removes item from current inventory location and adds it to destination * no anims involved * @return true if success, false otherwise **/ static proto native bool LocationSyncMoveEntity(notnull InventoryLocation src_loc, notnull InventoryLocation dst_loc); /** * @fn LocationSwap * @brief swaps two entities * @return true if success, false otherwise **/ static proto native bool LocationSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2); /** * @fn ServerLocationMoveEntity * @brief removes item from current inventory location and adds it to destination + sync via inventory command * @return true if success, false otherwise **/ static proto native bool ServerLocationMoveEntity(notnull EntityAI item, ParamsWriteContext ctx); /** * @fn ServerLocationSyncMoveEntity * @brief synchronously removes item from current inventory location and adds it to destination + sync via * inventory command * no anims involved * @return true if success, false otherwise **/ static proto native bool ServerLocationSyncMoveEntity(Man player, notnull EntityAI item, ParamsWriteContext ctx); /** * @fn ServerLocationSwap * @brief swaps two entities * @return true if success, false otherwise **/ static proto native bool ServerLocationSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2, ParamsWriteContext ctx); /** * @fn ServerHandEvent * @brief hand event to clients * @return true if success, false otherwise **/ static proto native bool ServerHandEvent(notnull Man player, notnull EntityAI item, ParamsWriteContext ctx); /** * @fn PrepareDropEntityPos * @brief Finds a transformation for the item to be dropped to * If the initial transforation overlaps with another conflicting entity (i.e. car) then the transform will be snapped to the nearest outer edge. * If no valid snapping transformation could be found, then the output from 'PlaceOnSurface' is used * @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' * @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 * @return true if valid transformation found near owner **/ static proto native bool PrepareDropEntityPos(EntityAI owner, notnull EntityAI item, out vector mat[4], bool useValuesInMatrix = false, int conflictCheckDepth = -1); static proto native bool TestDropEntityPos(EntityAI owner, notnull EntityAI item, out vector mat[4], bool useValuesInMatrix = false, int conflictCheckDepth = -1); /**@fn CanSwapEntities * @brief test if ordinary swap can be performed. * * Ordinary in the sense that the two items has equal sizes and can be swapped without * additional space. * * @param [in] item1 first item * @param [in] item2 second item * @return true if can be swapped */ static proto native bool CanSwapEntities(notnull EntityAI item1, notnull EntityAI item2); static bool CanSwapEntitiesEx(notnull EntityAI item1, notnull EntityAI item2) { int slot; InventoryLocation il1 = new InventoryLocation(); InventoryLocation il2 = new InventoryLocation(); item2.GetInventory().GetCurrentInventoryLocation(il2); slot = il2.GetSlot(); if (item1.CanBeSplit() && item1.GetQuantity() > item1.GetTargetQuantityMax(slot)) return false; item1.GetInventory().GetCurrentInventoryLocation(il1); slot = il1.GetSlot(); if (item2.CanBeSplit() && item2.GetQuantity() > item2.GetTargetQuantityMax(slot)) return false; if (!item1.CanSwapEntities(item2, il2, il1) || !item2.CanSwapEntities(item1, il1, il2)) { return false; } return CanSwapEntities(item1,item2); } /**@fn CanForceSwapEntities * @brief test if forced swap can be performed. * * @param [in] item1 is the forced item (primary) * @param [in] item1_dst optional destination for item1 (null if item1 goes to place of item2) * @param [in] item2 second item that will be replaced by the item1. (secondary) * @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 * @return true if can be force swapped */ static proto native bool CanForceSwapEntities(notnull EntityAI item1, InventoryLocation item1_dst, notnull EntityAI item2, out InventoryLocation item2_dst); static bool CanForceSwapEntitiesEx(notnull EntityAI item1, InventoryLocation item1_dst, notnull EntityAI item2, out InventoryLocation item2_dst) { if (!CanForceSwapEntities(item1, item1_dst, item2, item2_dst) ) return false; int slot; InventoryLocation il = new InventoryLocation; if (!item1.CanBeFSwaped()) return false; if ( item1_dst == null) { item2.GetInventory().GetCurrentInventoryLocation(il); slot = il.GetSlot(); } else { slot = item1_dst.GetSlot(); } if ( item1.GetQuantity() > item1.GetTargetQuantityMax(slot) ) return false; if ( item2_dst == null) { item1.GetInventory().GetCurrentInventoryLocation(il); slot = il.GetSlot(); } else { slot = item2_dst.GetSlot(); } if (!item1.CanSwapEntities(item2, item2_dst, item1_dst) || !item2.CanSwapEntities(item1, item1_dst, item2_dst)) { return false; } if ( item2.GetQuantity() > item2.GetTargetQuantityMax(slot) ) return false; return true; } proto native bool CanAddSwappedEntity(notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2); ///@{ reservations const int c_InventoryReservationTimeoutMS = 5000; const int c_InventoryReservationTimeoutShortMS = 3000; static proto native bool AddInventoryReservation(EntityAI item, InventoryLocation dst, int timeout_ms); bool AddInventoryReservationEx(EntityAI item, InventoryLocation dst, int timeout_ms) { if (GetGame().IsMultiplayer() && GetGame().IsServer() ) return true; bool ret_val = AddInventoryReservation(item, dst, timeout_ms); #ifdef ENABLE_LOGGING if ( LogManager.IsInventoryReservationLogEnable() ) { DayZPlayer player = GetGame().GetPlayer(); if ( player ) { if (item) Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "AddInventoryReservation", player.ToString() ); else Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "AddInventoryReservation", player.ToString() ); } } #endif return ret_val; } static proto native bool ExtendInventoryReservation(EntityAI item, InventoryLocation dst, int timeout_ms); bool ExtendInventoryReservationEx(EntityAI item, InventoryLocation dst, int timeout_ms) { if (GetGame().IsMultiplayer() && GetGame().IsServer() ) return true; bool ret_val = ExtendInventoryReservation(item,dst,timeout_ms); #ifdef ENABLE_LOGGING if ( LogManager.IsInventoryReservationLogEnable() ) { DayZPlayer player = GetGame().GetPlayer(); if ( player ) { if (item) Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ExtendInventoryReservation", player.ToString() ); else Debug.InventoryMoveLog("Reservation result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ExtendInventoryReservation", player.ToString() ); } } #endif return ret_val; } static proto native bool ClearInventoryReservation(EntityAI item, InventoryLocation dst); bool ClearInventoryReservationEx(EntityAI item, InventoryLocation dst) { if (GetGame().IsMultiplayer() && GetGame().IsServer() ) return true; bool ret_val = ClearInventoryReservation(item,dst); #ifdef ENABLE_LOGGING if ( LogManager.IsInventoryReservationLogEnable() ) { DayZPlayer player = GetGame().GetPlayer(); if ( player ) { if (item) Debug.InventoryMoveLog("Reservation cleared result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / " + item.ToString() + " / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ClearInventoryReservation", player.ToString() ); else Debug.InventoryMoveLog("Reservation cleared result: " + ret_val + " - STS = " + player.GetSimulationTimeStamp() + " / null / " + InventoryLocation.DumpToStringNullSafe(dst), "n/a" , "n/a", "ClearInventoryReservation", player.ToString() ); } } #endif return ret_val; } //! Internally: HasInventoryReservationEx(item, dst, FindInventoryReservationMode.LEGACY, FindInventoryReservationMode.LEGACY) static proto native bool HasInventoryReservation(EntityAI item, InventoryLocation dst); //! Internally: !HasInventoryReservationEx(item, dst, FindInventoryReservationMode.ITEM, FindInventoryReservationMode.DST) static proto native bool HasInventoryReservationCanAdd(EntityAI item, InventoryLocation dst); //! itemMode will iterate over item reservations, parentMode will iterate over parent reservations static proto native bool HasInventoryReservationEx(EntityAI item, InventoryLocation dst, FindInventoryReservationMode itemMode, FindInventoryReservationMode parentMode); static proto native bool GetInventoryReservationCount(EntityAI item, InventoryLocation dst); proto native int GetAnyInventoryReservationCount(); ///@} reservations ///@{ locks proto native bool CanLockInventoryWithKey(notnull EntityAI key); proto native bool CanUnlockInventoryWithKey(notnull EntityAI key); proto native void LockInventoryWithKey(notnull EntityAI key); proto native void UnlockInventoryWithKey(notnull EntityAI key); proto native bool HasKeys(); proto native void LockInventory(int lockType); proto native void UnlockInventory(int lockType); proto native int GetScriptLockCount(); proto native bool IsInventoryUnlocked(); proto native bool IsInventoryLocked(); proto native bool IsInventoryLockedForLockType(int lockType); proto native bool SetSlotLock(int slot, bool locked); proto native bool GetSlotLock(int slot); ///@} locks ///@{ anti-cheats const float c_MaxItemDistanceRadius = 2.5; static proto native bool CheckRequestSrc(notnull Man requestingPlayer, notnull InventoryLocation src, float radius); static proto native bool CheckDropRequest(notnull Man requestingPlayer, notnull InventoryLocation src, float radius); static proto native bool CheckTakeItemRequest(notnull Man requestingPlayer, notnull InventoryLocation src, notnull InventoryLocation dst, float radius); static proto native bool CheckMoveToDstRequest(notnull Man requestingPlayer, notnull InventoryLocation src, notnull InventoryLocation dst, float radius); static proto native bool CheckSwapItemsRequest(notnull Man requestingPlayer, notnull InventoryLocation src1, notnull InventoryLocation src2, notnull InventoryLocation dst1, notnull InventoryLocation dst2, float radius); static proto native bool CheckManipulatedObjectsDistances(notnull EntityAI e0, notnull EntityAI e1, float radius); ///@} anti-cheats ///@} Engine native functions /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///@{ script functions /**@fn Init * @brief called by engine right after creation of entity **/ void Init() { GetInventoryOwner().OnInventoryInit(); } /// db load hooks bool OnStoreLoad(ParamsReadContext ctx, int version) { return true; } void OnAfterStoreLoad(); /// db store hook void OnStoreSave(ParamsWriteContext ctx); void EEInit() { InventoryLocation src = new InventoryLocation; if (GetCurrentInventoryLocation(src)) { if (src.GetType() == InventoryLocationType.HANDS) { Man man = Man.Cast(src.GetParent()); if (man) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("Inventory::EEInit - Man=" + man + " item=" + this); man.GetHumanInventory().OnEntityInHandsCreated(src); } } } } void EEDelete(EntityAI parent) { EntityAI item = GetInventoryOwner(); Man player = item.GetHierarchyRootPlayer(); if (player) player.GetInventory().ClearInventoryReservationEx(item, null); } /**@fn CreateInInventory * @brief creates entity somewhere in inventory * * @param[in] type \p item type to be placed in inventory * @return created entity or null **/ EntityAI CreateInInventory(string type) { InventoryLocation loc = new InventoryLocation(); if (FindFirstFreeLocationForNewEntity(type, FindInventoryLocationType.CARGO | FindInventoryLocationType.ATTACHMENT, loc)) { switch (loc.GetType()) { case InventoryLocationType.ATTACHMENT: return loc.GetParent().GetInventory().CreateAttachmentEx(type, loc.GetSlot()); case InventoryLocationType.CARGO: return loc.GetParent().GetInventory().CreateEntityInCargoEx(type, loc.GetIdx(), loc.GetRow(), loc.GetCol(), loc.GetFlip()); default: Error("CreateInInventory: unknown location for item"); break; } } return null; } /** * @fn CanAddEntityToInventory * @brief ask inventory if item could be placed somewhere in inventory * * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are * traversed recursively from root to leafs. * * @param[in] item \p item to be placed in inventory * @return true if item can be added, false otherwise **/ bool CanAddEntityToInventory(notnull EntityAI item, int flag = FindInventoryLocationType.ANY) { InventoryLocation il = new InventoryLocation(); bool result = FindFreeLocationFor(item, flag, il); return result; } /** * @fn AddEntityToInventory * @brief add entity somewhere in inventory * * All available inventory locations (hands, cargo, attachments, proxycargo, ...) are * traversed recursively from root to leafs. * * @NOTE: AddEntityToInventory does not perform the checks the CanAddEntityToInventory does * (script conditions for example). * if not sure, always call CanAddEntityToInventory before AddEntityToInventory. * * @param[in] item \p item to be placed in inventory * @return true if item can be added, false otherwise **/ bool AddEntityToInventory(notnull EntityAI item) { InventoryLocation il = new InventoryLocation(); bool result = FindFreeLocationFor(item, FindInventoryLocationType.ANY, il); if (result) return LocationAddEntity(il); return result; } /** * @brief Returns if entity can be removed from its current location **/ bool CanRemoveEntity() { InventoryLocation il = new InventoryLocation; if (GetCurrentInventoryLocation(il)) return LocationCanRemoveEntity(il); return false; } // Script version of CanAddEntity* methods based on InventoryLocation /** * @fn CanAddEntityInto * @brief Asks inventory if item could be placed in inventory, hands, etc. * * Location is based on the flags parameter. This method is used in methods that are * asking for if item can be placed in specific location, like CanAddEntityToInventory, * CanAddEntityInHands, etc. * * @param[in] item \p item to be placed in inventory * @param[in] flags \p FindInventoryLocationType * @return true if item can be added, false otherwise */ bool CanAddEntityInto(notnull EntityAI item, FindInventoryLocationType flags = FindInventoryLocationType.ANY) { InventoryLocation loc = new InventoryLocation(); return FindFreeLocationFor(item, flags, loc) && !item.IsHologram(); } /** \brief Test if entity can be put to the inventory (Cargo, ProxyCargo, Attachment) */ bool CanAddEntityIntoInventory(notnull EntityAI item) { return CanAddEntityInto(item, FindInventoryLocationType.ANY_CARGO | FindInventoryLocationType.ATTACHMENT); } /** \brief Test if entity can be put into hands */ bool CanAddEntityIntoHands(notnull EntityAI item) { return CanAddEntityInto(item, FindInventoryLocationType.HANDS); } ///@{ synchronization bool OnInputUserDataProcess(ParamsReadContext ctx); bool OnInventoryJunctureFromServer(ParamsReadContext ctx); bool OnInventoryJunctureRepairFromServer(ParamsReadContext ctx); void OnInventoryJunctureFailureFromServer(ParamsReadContext ctx); void OnServerInventoryCommand(ParamsReadContext ctx); ///@} synchronization void OnInventoryFailure(InventoryCommandType type, InventoryValidationReason reason, InventoryLocation src, InventoryLocation dst); /** * @fn TakeEntityToInventory * @brief Put item anywhere into this entity (as attachment or into cargo, recursively) * @param[in] mode inventory mode * @param[in] flags preferred location, @see FindInventoryLocationType * @param[in] item item to be taken in inventory * @return true on success * * @NOTE: Predictive.* / Local.* functions: * a) 'Predictive' means that the operation uses Client-Side Prediction, i.e. the * action runs the same code on both, client AND server. * * b) 'Local' operation executes from where it is run, i.e. the operation from client * is NOT sent to server, and only client performs the inventory operation (TakeEntityTo.*) * this behaviour is necessary when inventory operations are ALREADY synchronized by another * means, like Actions. * For example @see ActionTakeItemToHands that performs OnCompleteServer * and OnCompleteClient with synchronization set to false in order to avoid duplicate * synchronization. **/ bool TakeEntityToInventory(InventoryMode mode, FindInventoryLocationType flags, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); if (FindFreeLocationFor(item, flags, dst)) return TakeToDst(mode, src, dst); if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Warning - no room for item"); return false; } Error("[inv] I::Take2Inv(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Error - src has no inventory location"); return false; } /// helper that finds location first, then moves the entity into it bool TakeEntityToTargetInventory(InventoryMode mode, notnull EntityAI target, FindInventoryLocationType flags, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); if (target.GetInventory().FindFreeLocationFor(item, flags, dst)) return TakeToDst(mode, src, dst); if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") target=" + target + " item=" + item + " Warning - no room for item in target"); return false; } Error("[inv] I::Take2Target(" + typename.EnumToString(InventoryMode, mode) + ") target=" + target + " item=" + item + " Error - src has no inventory location"); return false; } /**@fn TakeToDst * @brief move src to dst * @param[in] mode inventory mode * @param[in] src source location of the item * @param[in] dst destination location of the item **/ bool TakeToDst (InventoryMode mode, notnull InventoryLocation src, notnull InventoryLocation dst) { bool ret; switch (mode) { case InventoryMode.SERVER: ret = LocationSyncMoveEntity(src, dst); if (ret && dst.IsValid()) InventoryInputUserData.SendServerMove(null, InventoryCommandType.SYNC_MOVE, src, dst); return ret; case InventoryMode.LOCAL: ret = LocationSyncMoveEntity(src, dst); return ret; default: return false; } return false; } /**@fn TakeEntityToCargo * @brief moves item to cargo of this intentory * @param[in] mode inventory mode * @param[in] item item to be put in this inventory as cargo **/ bool TakeEntityToCargo(InventoryMode mode, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); return TakeEntityToInventory(mode, FindInventoryLocationType.CARGO, item); } /// Put item into into cargo of another entity bool TakeEntityToTargetCargo(InventoryMode mode, notnull EntityAI target, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetCgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + target); return TakeEntityToTargetInventory(mode, target, FindInventoryLocationType.CARGO, item); } /**@fn TakeEntityToCargoEx * @brief moves item on specific cargo location * @param[in] mode inventory mode * @param[in] item item to be put in this inventory as cargo **/ bool TakeEntityToCargoEx(InventoryMode mode, notnull EntityAI item, int idx, int row, int col) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " row=" + row + " col=" + col); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); dst.SetCargo(GetInventoryOwner(), item, idx, row, col, item.GetInventory().GetFlipCargo()); return TakeToDst(mode, src, dst); } Error("[inv] I::Take2Cgo(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " row=" + row + " col=" + col + " Error - src has no inventory location"); return false; } /// Put item into into cargo on specific cargo location of another entity bool TakeEntityToTargetCargoEx(InventoryMode mode, notnull CargoBase cargo, notnull EntityAI item, int row, int col) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetCgoEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + cargo.GetCargoOwner() + " row=" + row + " col=" + col); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); dst.SetCargoAuto(cargo, item, row, col, item.GetInventory().GetFlipCargo()); return TakeToDst(mode, src, dst); } Error("[inv] I::Take2TargetCgoEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + "to cargo of target=" + cargo.GetCargoOwner() + " row=" + row + " col=" + col); return false; } bool TakeEntityAsAttachmentEx(InventoryMode mode, notnull EntityAI item, int slot) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " slot=" + slot); return TakeEntityAsTargetAttachmentEx(mode, GetInventoryOwner(), item, slot); } /// put item as attachment of target bool TakeEntityAsTargetAttachmentEx(InventoryMode mode, notnull EntityAI target, notnull EntityAI item, int slot) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2TargetAttEx(" + typename.EnumToString(InventoryMode, mode) + ") as ATT of target=" + target + " item=" + item + " slot=" + slot); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { EntityAI att = target.GetInventory().FindAttachment(slot); if (att) { if (mode == InventoryMode.SERVER) { att.CombineItemsEx(item, true); } else { att.CombineItemsClient(item, true); } return true; } else if (item.CanBeSplit() && item.GetTargetQuantityMax(slot) < item.GetQuantity()) { if (mode == InventoryMode.SERVER) { item.SplitIntoStackMaxEx(target, slot); } else { item.SplitIntoStackMaxClient(target,slot); } return true; } else { InventoryLocation dst = new InventoryLocation(); dst.SetAttachment(target, item, slot); return TakeToDst(mode, src, dst); } } Error("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item + " Error - src has no inventory location"); return false; } bool TakeEntityAsAttachment(InventoryMode mode, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2Att(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); return TakeEntityToInventory(mode, FindInventoryLocationType.ATTACHMENT, item); } bool TakeEntityAsTargetAttachment(InventoryMode mode, notnull EntityAI target, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Take2AttEx(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); return TakeEntityToTargetInventory(mode, target, FindInventoryLocationType.ATTACHMENT, item); } /// helper function for swap static bool MakeDstForSwap(notnull InventoryLocation src1, notnull InventoryLocation src2, out InventoryLocation dst1, out InventoryLocation dst2) { if (dst1 == null) dst1 = new InventoryLocation(); dst1.Copy(src1); dst1.CopyLocationFrom(src2, false); dst1.SetFlip(src1.GetItem().GetInventory().GetFlipCargo()); // update flip flag from inventory item if (dst2 == null) dst2 = new InventoryLocation(); dst2.Copy(src2); dst2.CopyLocationFrom(src1, false); dst2.SetFlip(src2.GetItem().GetInventory().GetFlipCargo()); // update flip flag from inventory item return true; } /// helper function for swap static bool MakeSrcAndDstForSwap(notnull EntityAI item1, notnull EntityAI item2, out InventoryLocation src1, out InventoryLocation src2, out InventoryLocation dst1, out InventoryLocation dst2) { if (src1 == null) src1 = new InventoryLocation(); if (src2 == null) src2 = new InventoryLocation(); if (item1.GetInventory().GetCurrentInventoryLocation(src1) && item2.GetInventory().GetCurrentInventoryLocation(src2)) return MakeDstForSwap(src1, src2, dst1, dst2); return false; } /// helper function for ForceSwap static bool MakeSrcAndDstForForceSwap(notnull EntityAI item1, notnull EntityAI item2, out InventoryLocation src1, out InventoryLocation src2, out InventoryLocation dst1, notnull InventoryLocation dst2) { if (src1 == null) src1 = new InventoryLocation(); if (src2 == null) src2 = new InventoryLocation(); if (item1.GetInventory().GetCurrentInventoryLocation(src1) && item2.GetInventory().GetCurrentInventoryLocation(src2)) { // src1 -> dst_of(src2) if (dst1 == null) dst1 = new InventoryLocation(); dst1.Copy(src1); dst1.CopyLocationFrom(src2, false); dst1.SetFlip(dst1.GetItem().GetInventory().GetFlipCargo()); // src2 -> dst2 from user return true; } return false; } bool SwapEntities(InventoryMode mode, notnull EntityAI item1, notnull EntityAI item2) { return false; } bool ForceSwapEntities(InventoryMode mode, notnull EntityAI item1, notnull EntityAI item2, notnull InventoryLocation item2_dst) { return false; } static bool SetGroundPosByOwner(EntityAI owner, notnull EntityAI item, out InventoryLocation ground) { vector m4[4]; Math3D.MatrixIdentity4(m4); bool success = GameInventory.PrepareDropEntityPos(owner, item, m4, false, GameConstants.INVENTORY_ENTITY_DROP_OVERLAP_DEPTH); ground.SetGround(item, m4); return success; } bool DropEntity(InventoryMode mode, EntityAI owner, notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); if (!SetGroundPosByOwner(owner, item, dst)) { OnInventoryFailure(InventoryCommandType.SYNC_MOVE, InventoryValidationReason.DROP_PREVENTED, src, dst); return false; } return TakeToDst(mode, src, dst); } Error("DropEntity - No inventory location"); return false; } static bool SetGroundPosByTransform(EntityAI owner, notnull EntityAI item, out InventoryLocation ground, vector transform[4]) { bool success = GameInventory.PrepareDropEntityPos(owner, item, transform, true, GameConstants.INVENTORY_ENTITY_DROP_OVERLAP_DEPTH); ground.SetGround(item, transform); return success; } bool DropEntityWithTransform(InventoryMode mode, EntityAI owner, notnull EntityAI item, vector transform[4]) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); SetGroundPosByTransform(owner, item, dst, transform); return TakeToDst(mode, src, dst); } Error("DropEntityWithTransform - No inventory location"); return false; } static void SetGroundPosByOwnerBounds(EntityAI owner, notnull EntityAI item, out InventoryLocation ground, vector halfExtents, float angle, float cosAngle, float sinAngle) { vector m4[4]; owner.GetTransform(m4); vector randomPos = Vector(Math.RandomFloat(-halfExtents[0], halfExtents[0]), 0, Math.RandomFloat(-halfExtents[2], halfExtents[2])); randomPos = vector.RotateAroundZero(randomPos, vector.Up, cosAngle, sinAngle); float dist = randomPos[0] * m4[1][0] + randomPos[1] * m4[1][1] + randomPos[2] * m4[1][2]; m4[3][0] = m4[3][0] + randomPos[0]; m4[3][1] = m4[3][1] - dist + halfExtents[1]; m4[3][2] = m4[3][2] + randomPos[2]; item.PlaceOnSurfaceRotated(m4, Vector(m4[3][0], m4[3][1], m4[3][2])); ground.SetGround(item, m4); } bool DropEntityInBounds(InventoryMode mode, EntityAI owner, notnull EntityAI item, vector halfExtents, float angle, float cosAngle, float sinAngle) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::Drop(" + typename.EnumToString(InventoryMode, mode) + ") item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { InventoryLocation dst = new InventoryLocation(); SetGroundPosByOwnerBounds(owner, item, dst, halfExtents, angle, cosAngle, sinAngle); return TakeToDst(mode, src, dst); } Error("DropEntityInBounds - No inventory location"); return false; } bool LocalDestroyEntity(notnull EntityAI item) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::LocalDestroyEntity inv item=" + item); InventoryLocation src = new InventoryLocation(); if (item.GetInventory().GetCurrentInventoryLocation(src)) { if (src.GetType() == InventoryLocationType.HANDS) Error("[inv] Source location == HANDS, player has to handle this"); GetGame().ObjectDelete(src.GetItem()); return true; } Error("LocalDestroyEntity: No inventory location"); return false; } bool ReplaceItemWithNew(InventoryMode mode, ReplaceItemWithNewLambdaBase lambda) { InventoryLocation src = new InventoryLocation(); if (lambda.m_OldItem.GetInventory().GetCurrentInventoryLocation(src)) { if (LogManager.IsInventoryMoveLogEnable()) inventoryDebugPrint("[inv] I::ReplaceItemWithNew executing lambda=" + lambda + "on old_item=" + lambda.m_OldItem); lambda.Execute(); return true; } Error("[inv] I::ReplaceItemWithNew - no inventory location"); return false; } proto native bool GetFlipCargo(); proto native void SetFlipCargo(bool flip); proto native void FlipCargo(); proto native void ResetFlipCargo(); ///@} script functions }