2022-05-09 00:57:36 +00:00
# include "InventoryComponent.h"
2021-12-05 17:54:36 +00:00
# include <sstream>
# include "Entity.h"
# include "Item.h"
# include "Game.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
# include "CDClientManager.h"
2024-01-05 12:31:22 +00:00
# include "ObjectIDManager.h"
2021-12-05 17:54:36 +00:00
# include "MissionComponent.h"
# include "GameMessages.h"
# include "SkillComponent.h"
# include "Character.h"
# include "EntityManager.h"
# include "ItemSet.h"
# include "PetComponent.h"
# include "PossessorComponent.h"
# include "PossessableComponent.h"
# include "ModuleAssemblyComponent.h"
2023-12-04 15:20:41 +00:00
# include "HavokVehiclePhysicsComponent.h"
2021-12-05 17:54:36 +00:00
# include "CharacterComponent.h"
# include "dZoneManager.h"
# include "PropertyManagementComponent.h"
2022-02-10 05:21:10 +00:00
# include "DestroyableComponent.h"
2022-06-18 07:03:27 +00:00
# include "dConfig.h"
2022-07-17 01:36:09 +00:00
# include "eItemType.h"
2022-09-02 18:49:19 +00:00
# include "eUnequippableActiveType.h"
2022-12-21 22:33:41 +00:00
# include "CppScripts.h"
2021-12-05 17:54:36 +00:00
# include "eMissionTaskType.h"
2023-05-02 22:39:21 +00:00
# include "eStateChangeType.h"
# include "eUseItemResponse.h"
2024-01-12 17:39:51 +00:00
# include "Mail.h"
2021-12-05 17:54:36 +00:00
2023-03-17 14:36:21 +00:00
# include "CDComponentsRegistryTable.h"
# include "CDInventoryComponentTable.h"
# include "CDScriptComponentTable.h"
# include "CDObjectSkillsTable.h"
# include "CDSkillBehaviorTable.h"
2024-08-02 06:38:21 +00:00
# include "StringifiedEnum.h"
# include <ranges>
2023-03-17 14:36:21 +00:00
2024-04-08 20:13:49 +00:00
InventoryComponent : : InventoryComponent ( Entity * parent ) : Component ( parent ) {
2021-12-05 17:54:36 +00:00
this - > m_Dirty = true ;
this - > m_Equipped = { } ;
this - > m_Pushed = { } ;
this - > m_Consumable = LOT_NULL ;
this - > m_Pets = { } ;
const auto lot = parent - > GetLOT ( ) ;
if ( lot = = 1 ) {
2024-04-08 20:13:49 +00:00
auto * character = m_Parent - > GetCharacter ( ) ;
if ( character ) LoadXml ( character - > GetXMLDoc ( ) ) ;
2021-12-05 17:54:36 +00:00
CheckProxyIntegrity ( ) ;
return ;
}
2024-02-09 05:40:43 +00:00
auto * compRegistryTable = CDClientManager : : GetTable < CDComponentsRegistryTable > ( ) ;
2023-03-04 07:16:37 +00:00
const auto componentId = compRegistryTable - > GetByIDAndType ( lot , eReplicaComponentType : : INVENTORY ) ;
2021-12-05 17:54:36 +00:00
2024-02-09 05:40:43 +00:00
auto * inventoryComponentTable = CDClientManager : : GetTable < CDInventoryComponentTable > ( ) ;
2021-12-05 17:54:36 +00:00
auto items = inventoryComponentTable - > Query ( [ = ] ( const CDInventoryComponent entry ) { return entry . id = = componentId ; } ) ;
auto slot = 0u ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( const auto & item : items ) {
if ( ! item . equip | | ! Inventory : : IsValidItem ( item . itemid ) ) {
continue ;
}
2022-01-13 03:48:27 +00:00
2024-01-05 12:31:22 +00:00
const LWOOBJID id = ObjectIDManager : : GenerateObjectID ( ) ;
2021-12-05 17:54:36 +00:00
const auto & info = Inventory : : FindItemComponent ( item . itemid ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
UpdateSlot ( info . equipLocation , { id , static_cast < LOT > ( item . itemid ) , item . count , slot + + } ) ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
// Equip this items proxies.
auto subItems = info . subItems ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
subItems . erase ( std : : remove_if ( subItems . begin ( ) , subItems . end ( ) , : : isspace ) , subItems . end ( ) ) ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
if ( ! subItems . empty ( ) ) {
const auto subItemsSplit = GeneralUtils : : SplitString ( subItems , ' , ' ) ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
for ( auto proxyLotAsString : subItemsSplit ) {
const auto proxyLOT = static_cast < LOT > ( std : : stoi ( proxyLotAsString ) ) ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
const auto & proxyInfo = Inventory : : FindItemComponent ( proxyLOT ) ;
2024-01-05 12:31:22 +00:00
const LWOOBJID proxyId = ObjectIDManager : : GenerateObjectID ( ) ;
2022-09-02 18:49:19 +00:00
2022-07-30 00:00:36 +00:00
// Use item.count since we equip item.count number of the item this is a requested proxy of
2022-09-02 18:49:19 +00:00
UpdateSlot ( proxyInfo . equipLocation , { proxyId , proxyLOT , item . count , slot + + } ) ;
2022-07-30 00:00:36 +00:00
}
}
2021-12-05 17:54:36 +00:00
}
}
Inventory * InventoryComponent : : GetInventory ( const eInventoryType type ) {
const auto index = m_Inventories . find ( type ) ;
if ( index ! = m_Inventories . end ( ) ) {
return index - > second ;
}
// Create new empty inventory
uint32_t size = 240u ;
switch ( type ) {
case eInventoryType : : ITEMS :
size = 20u ;
break ;
2022-04-13 08:49:55 +00:00
case eInventoryType : : VAULT_MODELS :
2021-12-05 17:54:36 +00:00
case eInventoryType : : VAULT_ITEMS :
size = 40u ;
break ;
2022-04-23 11:16:10 +00:00
case eInventoryType : : VENDOR_BUYBACK :
size = 27u ;
break ;
2023-08-04 02:44:03 +00:00
case eInventoryType : : DONATION :
size = 24u ;
break ;
2021-12-05 17:54:36 +00:00
default :
break ;
}
auto * inventory = new Inventory ( type , size , { } , this ) ;
m_Inventories . insert_or_assign ( type , inventory ) ;
return inventory ;
}
const std : : map < eInventoryType , Inventory * > & InventoryComponent : : GetInventories ( ) const {
return m_Inventories ;
}
uint32_t InventoryComponent : : GetLotCount ( const LOT lot ) const {
uint32_t count = 0 ;
for ( const auto & inventory : m_Inventories ) {
count + = inventory . second - > GetLotCount ( lot ) ;
}
return count ;
}
uint32_t InventoryComponent : : GetLotCountNonTransfer ( LOT lot ) const {
uint32_t count = 0 ;
for ( const auto & inventory : m_Inventories ) {
if ( IsTransferInventory ( inventory . second - > GetType ( ) ) ) continue ;
count + = inventory . second - > GetLotCount ( lot ) ;
}
return count ;
}
const EquipmentMap & InventoryComponent : : GetEquippedItems ( ) const {
return m_Equipped ;
}
void InventoryComponent : : AddItem (
const LOT lot ,
const uint32_t count ,
2022-04-24 03:35:34 +00:00
eLootSourceType lootSourceType ,
2021-12-05 17:54:36 +00:00
eInventoryType inventoryType ,
const std : : vector < LDFBaseData * > & config ,
const LWOOBJID parent ,
const bool showFlyingLoot ,
bool isModMoveAndEquip ,
const LWOOBJID subKey ,
const eInventoryType inventorySourceType ,
const int32_t sourceType ,
const bool bound ,
int32_t preferredSlot ) {
if ( count = = 0 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Attempted to add 0 of item (%i) to the inventory! " , lot ) ;
2021-12-05 17:54:36 +00:00
return ;
}
if ( ! Inventory : : IsValidItem ( lot ) ) {
if ( lot > 0 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Attempted to add invalid item (%i) to the inventory! " , lot ) ;
2021-12-05 17:54:36 +00:00
}
return ;
}
if ( inventoryType = = INVALID ) {
inventoryType = Inventory : : FindInventoryTypeForLot ( lot ) ;
}
2023-03-04 07:16:37 +00:00
auto * missions = static_cast < MissionComponent * > ( this - > m_Parent - > GetComponent ( eReplicaComponentType : : MISSION ) ) ;
2021-12-05 17:54:36 +00:00
auto * inventory = GetInventory ( inventoryType ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( ! config . empty ( ) | | bound ) {
2022-04-24 03:35:34 +00:00
const auto slot = preferredSlot ! = - 1 & & inventory - > IsSlotEmpty ( preferredSlot ) ? preferredSlot : inventory - > FindEmptySlot ( ) ;
2021-12-05 17:54:36 +00:00
if ( slot = = - 1 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find empty slot for inventory (%i)! " , inventoryType ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-01-13 03:48:27 +00:00
2022-04-23 12:13:06 +00:00
auto * item = new Item ( lot , inventory , slot , count , config , parent , showFlyingLoot , isModMoveAndEquip , subKey , bound , lootSourceType ) ;
2021-12-05 17:54:36 +00:00
if ( missions ! = nullptr & & ! IsTransferInventory ( inventoryType ) ) {
missions - > Progress ( eMissionTaskType : : GATHER , lot , LWOOBJID_EMPTY , " " , count , IsTransferInventory ( inventorySourceType ) ) ;
}
return ;
}
const auto info = Inventory : : FindItemComponent ( lot ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
auto left = count ;
int32_t outOfSpace = 0 ;
auto stack = static_cast < uint32_t > ( info . stackSize ) ;
2022-11-30 09:04:46 +00:00
bool isBrick = inventoryType = = eInventoryType : : BRICKS | | ( stack = = 0 & & info . itemType = = 1 ) ;
2022-06-12 18:44:45 +00:00
// info.itemType of 1 is item type brick
2022-11-30 09:04:46 +00:00
if ( isBrick ) {
2022-11-26 10:30:53 +00:00
stack = UINT32_MAX ;
2021-12-05 17:54:36 +00:00
} else if ( stack = = 0 ) {
stack = 1 ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
auto * existing = FindItemByLot ( lot , inventoryType ) ;
if ( existing ! = nullptr ) {
const auto delta = std : : min < uint32_t > ( left , stack - existing - > GetCount ( ) ) ;
left - = delta ;
2022-04-24 03:35:34 +00:00
existing - > SetCount ( existing - > GetCount ( ) + delta , false , true , showFlyingLoot , lootSourceType ) ;
2021-12-05 17:54:36 +00:00
if ( isModMoveAndEquip ) {
existing - > Equip ( ) ;
isModMoveAndEquip = false ;
}
}
2022-11-26 10:30:53 +00:00
// If we have some leftover and we aren't bricks, make a new stack
2022-11-30 09:04:46 +00:00
while ( left > 0 & & ( ! isBrick | | ( isBrick & & ! existing ) ) ) {
2021-12-05 17:54:36 +00:00
const auto size = std : : min ( left , stack ) ;
left - = size ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
int32_t slot ;
if ( preferredSlot ! = - 1 & & inventory - > IsSlotEmpty ( preferredSlot ) ) {
slot = preferredSlot ;
preferredSlot = - 1 ;
} else {
slot = inventory - > FindEmptySlot ( ) ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( slot = = - 1 ) {
outOfSpace + = size ;
switch ( sourceType ) {
case 0 :
2024-01-12 17:39:51 +00:00
Mail : : SendMail ( LWOOBJID_EMPTY , " Darkflame Universe " , m_Parent , " Lost Reward " , " You received an item and didn't have room for it. " , lot , size ) ;
2021-12-05 17:54:36 +00:00
break ;
case 1 :
for ( size_t i = 0 ; i < size ; i + + ) {
GameMessages : : SendDropClientLoot ( this - > m_Parent , this - > m_Parent - > GetObjectID ( ) , lot , 0 , this - > m_Parent - > GetPosition ( ) , 1 ) ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
break ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
default :
break ;
}
continue ;
}
2022-04-23 12:13:06 +00:00
auto * item = new Item ( lot , inventory , slot , size , { } , parent , showFlyingLoot , isModMoveAndEquip , subKey , false , lootSourceType ) ;
2021-12-05 17:54:36 +00:00
isModMoveAndEquip = false ;
}
if ( missions ! = nullptr & & ! IsTransferInventory ( inventoryType ) ) {
missions - > Progress ( eMissionTaskType : : GATHER , lot , LWOOBJID_EMPTY , " " , count - outOfSpace , IsTransferInventory ( inventorySourceType ) ) ;
}
}
2023-11-15 01:38:52 +00:00
bool InventoryComponent : : RemoveItem ( const LOT lot , const uint32_t count , eInventoryType inventoryType , const bool ignoreBound , const bool silent ) {
2021-12-05 17:54:36 +00:00
if ( count = = 0 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Attempted to remove 0 of item (%i) from the inventory! " , lot ) ;
2023-11-15 01:38:52 +00:00
return false ;
2021-12-05 17:54:36 +00:00
}
2023-11-15 01:38:52 +00:00
if ( inventoryType = = INVALID ) inventoryType = Inventory : : FindInventoryTypeForLot ( lot ) ;
2021-12-05 17:54:36 +00:00
auto * inventory = GetInventory ( inventoryType ) ;
2023-11-15 01:38:52 +00:00
if ( ! inventory ) return false ;
2021-12-05 17:54:36 +00:00
auto left = std : : min < uint32_t > ( count , inventory - > GetLotCount ( lot ) ) ;
2023-11-15 01:38:52 +00:00
if ( left ! = count ) return false ;
2021-12-05 17:54:36 +00:00
while ( left > 0 ) {
auto * item = FindItemByLot ( lot , inventoryType , false , ignoreBound ) ;
2023-11-15 01:38:52 +00:00
if ( ! item ) break ;
2021-12-05 17:54:36 +00:00
const auto delta = std : : min < uint32_t > ( left , item - > GetCount ( ) ) ;
2023-11-15 01:38:52 +00:00
item - > SetCount ( item - > GetCount ( ) - delta , silent ) ;
2021-12-05 17:54:36 +00:00
left - = delta ;
}
2023-11-15 01:38:52 +00:00
return true ;
2021-12-05 17:54:36 +00:00
}
void InventoryComponent : : MoveItemToInventory ( Item * item , const eInventoryType inventory , const uint32_t count , const bool showFlyingLot , bool isModMoveAndEquip , const bool ignoreEquipped , const int32_t preferredSlot ) {
if ( item = = nullptr ) {
return ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
auto * origin = item - > GetInventory ( ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto lot = item - > GetLot ( ) ;
2022-01-13 03:48:27 +00:00
2022-12-04 22:25:25 +00:00
const auto subkey = item - > GetSubKey ( ) ;
if ( subkey = = LWOOBJID_EMPTY & & item - > GetConfig ( ) . empty ( ) & & ( ! item - > GetBound ( ) | | ( item - > GetBound ( ) & & item - > GetInfo ( ) . isBOP ) ) ) {
2021-12-05 17:54:36 +00:00
auto left = std : : min < uint32_t > ( count , origin - > GetLotCount ( lot ) ) ;
while ( left > 0 ) {
if ( item = = nullptr ) {
item = origin - > FindItemByLot ( lot , false ) ;
if ( item = = nullptr ) {
break ;
}
}
const auto delta = std : : min < uint32_t > ( item - > GetCount ( ) , left ) ;
left - = delta ;
2023-05-02 22:39:21 +00:00
AddItem ( lot , delta , eLootSourceType : : NONE , inventory , { } , LWOOBJID_EMPTY , showFlyingLot , isModMoveAndEquip , LWOOBJID_EMPTY , origin - > GetType ( ) , 0 , false , preferredSlot ) ;
2021-12-05 17:54:36 +00:00
item - > SetCount ( item - > GetCount ( ) - delta , false , false ) ;
isModMoveAndEquip = false ;
}
} else {
std : : vector < LDFBaseData * > config ;
for ( auto * const data : item - > GetConfig ( ) ) {
config . push_back ( data - > Copy ( ) ) ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto delta = std : : min < uint32_t > ( item - > GetCount ( ) , count ) ;
2023-05-02 22:39:21 +00:00
AddItem ( lot , delta , eLootSourceType : : NONE , inventory , config , LWOOBJID_EMPTY , showFlyingLot , isModMoveAndEquip , subkey , origin - > GetType ( ) , 0 , item - > GetBound ( ) , preferredSlot ) ;
2021-12-05 17:54:36 +00:00
item - > SetCount ( item - > GetCount ( ) - delta , false , false ) ;
}
auto * missionComponent = m_Parent - > GetComponent < MissionComponent > ( ) ;
if ( missionComponent ! = nullptr ) {
if ( IsTransferInventory ( inventory ) ) {
missionComponent - > Progress ( eMissionTaskType : : GATHER , lot , LWOOBJID_EMPTY , " " , - static_cast < int32_t > ( count ) ) ;
}
}
}
void InventoryComponent : : MoveStack ( Item * item , const eInventoryType inventory , const uint32_t slot ) {
if ( inventory ! = INVALID & & item - > GetInventory ( ) - > GetType ( ) ! = inventory ) {
auto * newInventory = GetInventory ( inventory ) ;
item - > SetInventory ( newInventory ) ;
}
item - > SetSlot ( slot ) ;
}
Item * InventoryComponent : : FindItemById ( const LWOOBJID id ) const {
for ( const auto & inventory : m_Inventories ) {
auto * item = inventory . second - > FindItemById ( id ) ;
if ( item ! = nullptr ) {
return item ;
}
}
return nullptr ;
}
Item * InventoryComponent : : FindItemByLot ( const LOT lot , eInventoryType inventoryType , const bool ignoreEquipped , const bool ignoreBound ) {
if ( inventoryType = = INVALID ) {
inventoryType = Inventory : : FindInventoryTypeForLot ( lot ) ;
}
auto * inventory = GetInventory ( inventoryType ) ;
return inventory - > FindItemByLot ( lot , ignoreEquipped , ignoreBound ) ;
}
2022-01-13 03:48:27 +00:00
Item * InventoryComponent : : FindItemBySubKey ( LWOOBJID id , eInventoryType inventoryType ) {
2021-12-05 17:54:36 +00:00
if ( inventoryType = = INVALID ) {
for ( const auto & inventory : m_Inventories ) {
auto * item = inventory . second - > FindItemBySubKey ( id ) ;
if ( item ! = nullptr ) {
return item ;
}
}
return nullptr ;
} else {
return GetInventory ( inventoryType ) - > FindItemBySubKey ( id ) ;
}
}
bool InventoryComponent : : HasSpaceForLoot ( const std : : unordered_map < LOT , int32_t > & loot ) {
std : : unordered_map < eInventoryType , int32_t > spaceOffset { } ;
uint32_t slotsNeeded = 0 ;
for ( const auto & pair : loot ) {
const auto inventoryType = Inventory : : FindInventoryTypeForLot ( pair . first ) ;
if ( inventoryType = = BRICKS ) {
continue ;
}
auto * inventory = GetInventory ( inventoryType ) ;
if ( inventory = = nullptr ) {
return false ;
}
const auto info = Inventory : : FindItemComponent ( pair . first ) ;
auto stack = static_cast < uint32_t > ( info . stackSize ) ;
auto left = pair . second ;
auto * partial = inventory - > FindItemByLot ( pair . first ) ;
if ( partial ! = nullptr & & partial - > GetCount ( ) < stack ) {
left - = stack - partial - > GetCount ( ) ;
}
auto requiredSlots = std : : ceil ( static_cast < double > ( left ) / stack ) ;
const auto & offsetIter = spaceOffset . find ( inventoryType ) ;
auto freeSpace = inventory - > GetEmptySlots ( ) - ( offsetIter = = spaceOffset . end ( ) ? 0 : offsetIter - > second ) ;
if ( requiredSlots > freeSpace ) {
slotsNeeded + = requiredSlots - freeSpace ;
}
spaceOffset [ inventoryType ] = offsetIter = = spaceOffset . end ( ) ? requiredSlots : offsetIter - > second + requiredSlots ;
}
if ( slotsNeeded > 0 ) {
GameMessages : : SendNotifyNotEnoughInvSpace ( m_Parent - > GetObjectID ( ) , slotsNeeded , ITEMS , m_Parent - > GetSystemAddress ( ) ) ;
return false ;
}
return true ;
}
2024-04-08 20:13:49 +00:00
void InventoryComponent : : LoadXml ( const tinyxml2 : : XMLDocument & document ) {
2021-12-05 17:54:36 +00:00
LoadPetXml ( document ) ;
2024-04-08 20:13:49 +00:00
auto * inventoryElement = document . FirstChildElement ( " obj " ) - > FirstChildElement ( " inv " ) ;
2021-12-05 17:54:36 +00:00
if ( inventoryElement = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'inv' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
auto * bags = inventoryElement - > FirstChildElement ( " bag " ) ;
if ( bags = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'bags' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2024-08-02 06:38:21 +00:00
auto * const groups = inventoryElement - > FirstChildElement ( " grps " ) ;
if ( groups ) {
LoadGroupXml ( * groups ) ;
}
2021-12-05 17:54:36 +00:00
m_Consumable = inventoryElement - > IntAttribute ( " csl " , LOT_NULL ) ;
auto * bag = bags - > FirstChildElement ( ) ;
while ( bag ! = nullptr ) {
unsigned int type ;
unsigned int size ;
bag - > QueryAttribute ( " t " , & type ) ;
bag - > QueryAttribute ( " m " , & size ) ;
auto * inventory = GetInventory ( static_cast < eInventoryType > ( type ) ) ;
inventory - > SetSize ( size ) ;
bag = bag - > NextSiblingElement ( ) ;
}
auto * items = inventoryElement - > FirstChildElement ( " items " ) ;
if ( items = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'items' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
bag = items - > FirstChildElement ( ) ;
while ( bag ! = nullptr ) {
unsigned int type ;
bag - > QueryAttribute ( " t " , & type ) ;
auto * inventory = GetInventory ( static_cast < eInventoryType > ( type ) ) ;
if ( inventory = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find inventory (%i)! " , type ) ;
2021-12-05 17:54:36 +00:00
return ;
}
auto * itemElement = bag - > FirstChildElement ( ) ;
while ( itemElement ! = nullptr ) {
LWOOBJID id ;
LOT lot ;
bool equipped ;
unsigned int slot ;
unsigned int count ;
bool bound ;
LWOOBJID subKey = LWOOBJID_EMPTY ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
itemElement - > QueryAttribute ( " id " , & id ) ;
itemElement - > QueryAttribute ( " l " , & lot ) ;
itemElement - > QueryAttribute ( " eq " , & equipped ) ;
itemElement - > QueryAttribute ( " s " , & slot ) ;
itemElement - > QueryAttribute ( " c " , & count ) ;
itemElement - > QueryAttribute ( " b " , & bound ) ;
itemElement - > QueryAttribute ( " sk " , & subKey ) ;
// Begin custom xml
auto parent = LWOOBJID_EMPTY ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
itemElement - > QueryAttribute ( " parent " , & parent ) ;
// End custom xml
2024-05-23 00:06:52 +00:00
auto * item = new Item ( id , lot , inventory , slot , count , bound , { } , parent , subKey ) ;
2021-12-05 17:54:36 +00:00
2024-05-23 00:06:52 +00:00
item - > LoadConfigXml ( * itemElement ) ;
2021-12-05 17:54:36 +00:00
if ( equipped ) {
const auto info = Inventory : : FindItemComponent ( lot ) ;
UpdateSlot ( info . equipLocation , { item - > GetId ( ) , item - > GetLot ( ) , item - > GetCount ( ) , item - > GetSlot ( ) } ) ;
AddItemSkills ( item - > GetLot ( ) ) ;
}
itemElement = itemElement - > NextSiblingElement ( ) ;
}
bag = bag - > NextSiblingElement ( ) ;
}
for ( const auto inventory : m_Inventories ) {
const auto itemCount = inventory . second - > GetItems ( ) . size ( ) ;
if ( inventory . second - > GetSize ( ) < itemCount ) {
inventory . second - > SetSize ( itemCount ) ;
}
}
}
2024-04-08 20:13:49 +00:00
void InventoryComponent : : UpdateXml ( tinyxml2 : : XMLDocument & document ) {
2021-12-05 17:54:36 +00:00
UpdatePetXml ( document ) ;
2024-04-08 20:13:49 +00:00
auto * inventoryElement = document . FirstChildElement ( " obj " ) - > FirstChildElement ( " inv " ) ;
2021-12-05 17:54:36 +00:00
if ( inventoryElement = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'inv' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-11-28 00:48:46 +00:00
std : : vector < Inventory * > inventoriesToSave ;
2021-12-05 17:54:36 +00:00
2022-11-28 00:48:46 +00:00
// Need to prevent some transfer inventories from being saved
2021-12-05 17:54:36 +00:00
for ( const auto & pair : this - > m_Inventories ) {
auto * inventory = pair . second ;
2022-11-28 00:48:46 +00:00
if ( inventory - > GetType ( ) = = VENDOR_BUYBACK | | inventory - > GetType ( ) = = eInventoryType : : MODELS_IN_BBB ) {
2021-12-05 17:54:36 +00:00
continue ;
}
2022-11-28 00:48:46 +00:00
inventoriesToSave . push_back ( inventory ) ;
2021-12-05 17:54:36 +00:00
}
inventoryElement - > SetAttribute ( " csl " , m_Consumable ) ;
auto * bags = inventoryElement - > FirstChildElement ( " bag " ) ;
if ( bags = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'bags' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
bags - > DeleteChildren ( ) ;
2022-11-28 00:48:46 +00:00
for ( const auto * inventory : inventoriesToSave ) {
2024-04-08 20:13:49 +00:00
auto * bag = document . NewElement ( " b " ) ;
2021-12-05 17:54:36 +00:00
bag - > SetAttribute ( " t " , inventory - > GetType ( ) ) ;
bag - > SetAttribute ( " m " , static_cast < unsigned int > ( inventory - > GetSize ( ) ) ) ;
bags - > LinkEndChild ( bag ) ;
}
2022-01-13 03:48:27 +00:00
2024-08-02 06:38:21 +00:00
auto * groups = inventoryElement - > FirstChildElement ( " grps " ) ;
if ( groups ) {
groups - > DeleteChildren ( ) ;
} else {
groups = inventoryElement - > InsertNewChildElement ( " grps " ) ;
}
UpdateGroupXml ( * groups ) ;
2021-12-05 17:54:36 +00:00
auto * items = inventoryElement - > FirstChildElement ( " items " ) ;
if ( items = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find 'items' xml element! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
items - > DeleteChildren ( ) ;
2022-11-28 00:48:46 +00:00
for ( auto * inventory : inventoriesToSave ) {
2021-12-05 17:54:36 +00:00
if ( inventory - > GetSize ( ) = = 0 ) {
continue ;
}
2024-04-08 20:13:49 +00:00
auto * bagElement = document . NewElement ( " in " ) ;
2021-12-05 17:54:36 +00:00
bagElement - > SetAttribute ( " t " , inventory - > GetType ( ) ) ;
for ( const auto & pair : inventory - > GetItems ( ) ) {
auto * item = pair . second ;
2024-04-08 20:13:49 +00:00
auto * itemElement = document . NewElement ( " i " ) ;
2021-12-05 17:54:36 +00:00
itemElement - > SetAttribute ( " l " , item - > GetLot ( ) ) ;
itemElement - > SetAttribute ( " id " , item - > GetId ( ) ) ;
itemElement - > SetAttribute ( " s " , static_cast < unsigned int > ( item - > GetSlot ( ) ) ) ;
itemElement - > SetAttribute ( " c " , static_cast < unsigned int > ( item - > GetCount ( ) ) ) ;
itemElement - > SetAttribute ( " b " , item - > GetBound ( ) ) ;
itemElement - > SetAttribute ( " eq " , item - > IsEquipped ( ) ) ;
itemElement - > SetAttribute ( " sk " , item - > GetSubKey ( ) ) ;
// Begin custom xml
itemElement - > SetAttribute ( " parent " , item - > GetParent ( ) ) ;
// End custom xml
2024-05-23 00:06:52 +00:00
item - > SaveConfigXml ( * itemElement ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
bagElement - > LinkEndChild ( itemElement ) ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
items - > LinkEndChild ( bagElement ) ;
}
}
2024-02-27 07:25:44 +00:00
void InventoryComponent : : Serialize ( RakNet : : BitStream & outBitStream , const bool bIsInitialUpdate ) {
2021-12-05 17:54:36 +00:00
if ( bIsInitialUpdate | | m_Dirty ) {
2024-02-27 07:25:44 +00:00
outBitStream . Write ( true ) ;
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write < uint32_t > ( m_Equipped . size ( ) ) ;
2021-12-05 17:54:36 +00:00
for ( const auto & pair : m_Equipped ) {
const auto item = pair . second ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( bIsInitialUpdate ) {
AddItemSkills ( item . lot ) ;
}
2024-02-27 07:25:44 +00:00
outBitStream . Write ( item . id ) ;
outBitStream . Write ( item . lot ) ;
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write0 ( ) ;
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write ( item . count > 0 ) ;
if ( item . count > 0 ) outBitStream . Write ( item . count ) ;
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write ( item . slot ! = 0 ) ;
if ( item . slot ! = 0 ) outBitStream . Write < uint16_t > ( item . slot ) ;
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write0 ( ) ;
2022-01-13 03:48:27 +00:00
2022-05-09 00:57:36 +00:00
bool flag = ! item . config . empty ( ) ;
2024-02-27 07:25:44 +00:00
outBitStream . Write ( flag ) ;
2022-05-09 00:57:36 +00:00
if ( flag ) {
RakNet : : BitStream ldfStream ;
ldfStream . Write < int32_t > ( item . config . size ( ) ) ; // Key count
for ( LDFBaseData * data : item . config ) {
if ( data - > GetKey ( ) = = u " assemblyPartLOTs " ) {
std : : string newRocketStr = data - > GetValueAsString ( ) + " ; " ;
GeneralUtils : : ReplaceInString ( newRocketStr , " + " , " ; " ) ;
LDFData < std : : u16string > * ldf_data = new LDFData < std : : u16string > ( u " assemblyPartLOTs " , GeneralUtils : : ASCIIToUTF16 ( newRocketStr ) ) ;
2024-02-26 14:15:29 +00:00
ldf_data - > WriteToPacket ( ldfStream ) ;
2022-05-09 00:57:36 +00:00
delete ldf_data ;
} else {
2024-02-26 14:15:29 +00:00
data - > WriteToPacket ( ldfStream ) ;
2022-05-09 00:57:36 +00:00
}
}
2024-02-27 07:25:44 +00:00
outBitStream . Write ( ldfStream . GetNumberOfBytesUsed ( ) + 1 ) ;
outBitStream . Write < uint8_t > ( 0 ) ; // Don't compress
outBitStream . Write ( ldfStream ) ;
2022-05-09 00:57:36 +00:00
}
2022-07-25 02:26:51 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write1 ( ) ;
2021-12-05 17:54:36 +00:00
}
m_Dirty = false ;
} else {
2024-02-27 07:25:44 +00:00
outBitStream . Write ( false ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-13 03:48:27 +00:00
2024-02-27 07:25:44 +00:00
outBitStream . Write ( false ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : Update ( float deltaTime ) {
2021-12-05 17:54:36 +00:00
for ( auto * set : m_Itemsets ) {
set - > Update ( deltaTime ) ;
}
}
void InventoryComponent : : UpdateSlot ( const std : : string & location , EquippedItem item , bool keepCurrent ) {
const auto index = m_Equipped . find ( location ) ;
if ( index ! = m_Equipped . end ( ) ) {
if ( keepCurrent ) {
m_Equipped . insert_or_assign ( location + std : : to_string ( m_Equipped . size ( ) ) , item ) ;
m_Dirty = true ;
return ;
}
auto * old = FindItemById ( index - > second . id ) ;
if ( old ! = nullptr ) {
UnEquipItem ( old ) ;
}
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
m_Equipped . insert_or_assign ( location , item ) ;
m_Dirty = true ;
}
void InventoryComponent : : RemoveSlot ( const std : : string & location ) {
if ( m_Equipped . find ( location ) = = m_Equipped . end ( ) ) {
return ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
m_Equipped . erase ( location ) ;
m_Dirty = true ;
}
void InventoryComponent : : EquipItem ( Item * item , const bool skipChecks ) {
2022-09-02 18:49:19 +00:00
if ( ! Inventory : : IsValidItem ( item - > GetLot ( ) ) ) return ;
2021-12-05 17:54:36 +00:00
// Temp items should be equippable but other transfer items shouldn't be (for example the instruments in RB)
if ( item - > IsEquipped ( )
| | ( item - > GetInventory ( ) - > GetType ( ) ! = TEMP_ITEMS & & IsTransferInventory ( item - > GetInventory ( ) - > GetType ( ) ) )
| | IsPet ( item - > GetSubKey ( ) ) ) {
return ;
}
auto * character = m_Parent - > GetCharacter ( ) ;
if ( character ! = nullptr & & ! skipChecks ) {
// Hacky proximity rocket
if ( item - > GetLot ( ) = = 6416 ) {
2023-07-15 20:56:33 +00:00
const auto rocketLauchPads = Game : : entityManager - > GetEntitiesByComponent ( eReplicaComponentType : : ROCKET_LAUNCH ) ;
2021-12-05 17:54:36 +00:00
const auto position = m_Parent - > GetPosition ( ) ;
2023-06-18 00:20:05 +00:00
for ( auto * launchPad : rocketLauchPads ) {
if ( ! launchPad ) continue ;
auto prereq = launchPad - > GetVarAsString ( u " rocketLaunchPreCondition " ) ;
if ( ! prereq . empty ( ) ) {
PreconditionExpression expression ( prereq ) ;
if ( ! expression . Check ( m_Parent ) ) continue ;
}
if ( Vector3 : : DistanceSquared ( launchPad - > GetPosition ( ) , position ) > 13 * 13 ) continue ;
2021-12-05 17:54:36 +00:00
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
2022-09-02 18:49:19 +00:00
if ( characterComponent ! = nullptr ) characterComponent - > SetLastRocketItemID ( item - > GetId ( ) ) ;
2021-12-05 17:54:36 +00:00
2023-06-18 00:20:05 +00:00
launchPad - > OnUse ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
break ;
}
return ;
}
const auto building = character - > GetBuildMode ( ) ;
const auto type = static_cast < eItemType > ( item - > GetInfo ( ) . itemType ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
2022-09-02 18:49:19 +00:00
if ( ! building & & ( item - > GetLot ( ) = = 6086 | | type = = eItemType : : LOOT_MODEL | | type = = eItemType : : VEHICLE ) ) return ;
2021-12-05 17:54:36 +00:00
2022-07-17 01:36:09 +00:00
if ( type ! = eItemType : : LOOT_MODEL & & type ! = eItemType : : MODEL ) {
2021-12-05 17:54:36 +00:00
if ( ! item - > GetBound ( ) & & ! item - > GetPreconditionExpression ( ) - > Check ( m_Parent ) ) {
return ;
}
}
}
const auto lot = item - > GetLot ( ) ;
CheckItemSet ( lot ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( auto * set : m_Itemsets ) {
set - > OnEquip ( lot ) ;
}
2022-01-13 03:48:27 +00:00
2022-09-02 18:49:19 +00:00
if ( item - > GetInfo ( ) . isBOE ) item - > SetBound ( true ) ;
2021-12-05 17:54:36 +00:00
GenerateProxies ( item ) ;
2022-07-25 02:26:51 +00:00
2022-05-09 00:57:36 +00:00
UpdateSlot ( item - > GetInfo ( ) . equipLocation , { item - > GetId ( ) , item - > GetLot ( ) , item - > GetCount ( ) , item - > GetSlot ( ) , item - > GetConfig ( ) } ) ;
2022-01-13 03:48:27 +00:00
2022-03-27 22:24:06 +00:00
ApplyBuff ( item ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
AddItemSkills ( item - > GetLot ( ) ) ;
2022-12-21 22:33:41 +00:00
EquipScripts ( item ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
void InventoryComponent : : UnEquipItem ( Item * item ) {
if ( ! item - > IsEquipped ( ) ) {
return ;
}
const auto lot = item - > GetLot ( ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( ! Inventory : : IsValidItem ( lot ) ) {
return ;
}
CheckItemSet ( lot ) ;
for ( auto * set : m_Itemsets ) {
set - > OnUnEquip ( lot ) ;
}
2022-03-27 22:24:06 +00:00
RemoveBuff ( item ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
RemoveItemSkills ( item - > GetLot ( ) ) ;
RemoveSlot ( item - > GetInfo ( ) . equipLocation ) ;
2022-01-13 03:48:27 +00:00
2022-12-21 22:33:41 +00:00
UnequipScripts ( item ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
// Trigger property event
if ( PropertyManagementComponent : : Instance ( ) ! = nullptr & & item - > GetCount ( ) > 0 & & Inventory : : FindInventoryTypeForLot ( item - > GetLot ( ) ) = = MODELS ) {
PropertyManagementComponent : : Instance ( ) - > GetParent ( ) - > OnZonePropertyModelRemovedWhileEquipped ( m_Parent ) ;
2023-07-17 22:55:33 +00:00
Game : : zoneManager - > GetZoneControlObject ( ) - > OnZonePropertyModelRemovedWhileEquipped ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
2024-05-31 04:53:03 +00:00
PurgeProxies ( item ) ;
2021-12-05 17:54:36 +00:00
}
2022-12-21 22:33:41 +00:00
void InventoryComponent : : EquipScripts ( Item * equippedItem ) {
2024-02-09 05:40:43 +00:00
CDComponentsRegistryTable * compRegistryTable = CDClientManager : : GetTable < CDComponentsRegistryTable > ( ) ;
2022-12-21 22:33:41 +00:00
if ( ! compRegistryTable ) return ;
2023-03-04 07:16:37 +00:00
int32_t scriptComponentID = compRegistryTable - > GetByIDAndType ( equippedItem - > GetLot ( ) , eReplicaComponentType : : SCRIPT , - 1 ) ;
2022-12-21 22:33:41 +00:00
if ( scriptComponentID > - 1 ) {
2024-02-09 05:40:43 +00:00
CDScriptComponentTable * scriptCompTable = CDClientManager : : GetTable < CDScriptComponentTable > ( ) ;
2022-12-21 22:33:41 +00:00
CDScriptComponent scriptCompData = scriptCompTable - > GetByID ( scriptComponentID ) ;
auto * itemScript = CppScripts : : GetScript ( m_Parent , scriptCompData . script_name ) ;
if ( ! itemScript ) {
2023-10-21 23:31:55 +00:00
LOG ( " null script? " ) ;
2022-12-21 22:33:41 +00:00
}
itemScript - > OnFactionTriggerItemEquipped ( m_Parent , equippedItem - > GetId ( ) ) ;
}
}
void InventoryComponent : : UnequipScripts ( Item * unequippedItem ) {
2024-02-09 05:40:43 +00:00
CDComponentsRegistryTable * compRegistryTable = CDClientManager : : GetTable < CDComponentsRegistryTable > ( ) ;
2022-12-21 22:33:41 +00:00
if ( ! compRegistryTable ) return ;
2023-03-04 07:16:37 +00:00
int32_t scriptComponentID = compRegistryTable - > GetByIDAndType ( unequippedItem - > GetLot ( ) , eReplicaComponentType : : SCRIPT , - 1 ) ;
2022-12-21 22:33:41 +00:00
if ( scriptComponentID > - 1 ) {
2024-02-09 05:40:43 +00:00
CDScriptComponentTable * scriptCompTable = CDClientManager : : GetTable < CDScriptComponentTable > ( ) ;
2022-12-21 22:33:41 +00:00
CDScriptComponent scriptCompData = scriptCompTable - > GetByID ( scriptComponentID ) ;
auto * itemScript = CppScripts : : GetScript ( m_Parent , scriptCompData . script_name ) ;
if ( ! itemScript ) {
2023-10-21 23:31:55 +00:00
LOG ( " null script? " ) ;
2022-12-21 22:33:41 +00:00
}
itemScript - > OnFactionTriggerItemUnequipped ( m_Parent , unequippedItem - > GetId ( ) ) ;
}
}
2022-09-02 18:49:19 +00:00
void InventoryComponent : : HandlePossession ( Item * item ) {
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
if ( ! characterComponent ) return ;
auto * possessorComponent = m_Parent - > GetComponent < PossessorComponent > ( ) ;
if ( ! possessorComponent ) return ;
// Don't do anything if we are busy dismounting
if ( possessorComponent - > GetIsDismounting ( ) ) return ;
// Check to see if we are already mounting something
2023-07-15 20:56:33 +00:00
auto * currentlyPossessedEntity = Game : : entityManager - > GetEntity ( possessorComponent - > GetPossessable ( ) ) ;
2022-09-02 18:49:19 +00:00
auto currentlyPossessedItem = possessorComponent - > GetMountItemID ( ) ;
if ( currentlyPossessedItem ) {
if ( currentlyPossessedEntity ) possessorComponent - > Dismount ( currentlyPossessedEntity ) ;
return ;
}
2023-01-07 05:59:19 +00:00
GameMessages : : SendSetStunned ( m_Parent - > GetObjectID ( ) , eStateChangeType : : PUSH , m_Parent - > GetSystemAddress ( ) , LWOOBJID_EMPTY , true , false , true , false , false , false , false , true , true , true , true , true , true , true , true , true ) ;
2022-09-02 18:49:19 +00:00
// Set the mount Item ID so that we know what were handling
possessorComponent - > SetMountItemID ( item - > GetId ( ) ) ;
GameMessages : : SendSetMountInventoryID ( m_Parent , item - > GetId ( ) , UNASSIGNED_SYSTEM_ADDRESS ) ;
// Create entity to mount
auto startRotation = m_Parent - > GetRotation ( ) ;
EntityInfo info { } ;
info . lot = item - > GetLot ( ) ;
info . pos = m_Parent - > GetPosition ( ) ;
info . rot = startRotation ;
info . spawnerID = m_Parent - > GetObjectID ( ) ;
2023-07-15 20:56:33 +00:00
auto * mount = Game : : entityManager - > CreateEntity ( info , nullptr , m_Parent ) ;
2022-09-02 18:49:19 +00:00
// Check to see if the mount is a vehicle, if so, flip it
2023-12-04 15:20:41 +00:00
auto * vehicleComponent = mount - > GetComponent < HavokVehiclePhysicsComponent > ( ) ;
2023-07-03 16:58:49 +00:00
if ( vehicleComponent ) characterComponent - > SetIsRacing ( true ) ;
2022-09-02 18:49:19 +00:00
// Setup the destroyable stats
auto * destroyableComponent = mount - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyableComponent ) {
destroyableComponent - > SetIsImmune ( true ) ;
}
// Mount it
auto * possessableComponent = mount - > GetComponent < PossessableComponent > ( ) ;
if ( possessableComponent ) {
possessableComponent - > SetIsItemSpawned ( true ) ;
possessableComponent - > SetPossessor ( m_Parent - > GetObjectID ( ) ) ;
// Possess it
possessorComponent - > SetPossessable ( mount - > GetObjectID ( ) ) ;
possessorComponent - > SetPossessableType ( possessableComponent - > GetPossessionType ( ) ) ;
}
GameMessages : : SendSetJetPackMode ( m_Parent , false ) ;
// Make it go to the client
2023-07-15 20:56:33 +00:00
Game : : entityManager - > ConstructEntity ( mount ) ;
2022-09-02 18:49:19 +00:00
// Update the possessor
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2022-09-02 18:49:19 +00:00
// have to unlock the input so it vehicle can be driven
if ( vehicleComponent ) GameMessages : : SendVehicleUnlockInput ( mount - > GetObjectID ( ) , false , m_Parent - > GetSystemAddress ( ) ) ;
GameMessages : : SendMarkInventoryItemAsActive ( m_Parent - > GetObjectID ( ) , true , eUnequippableActiveType : : MOUNT , item - > GetId ( ) , m_Parent - > GetSystemAddress ( ) ) ;
}
2022-03-27 22:24:06 +00:00
void InventoryComponent : : ApplyBuff ( Item * item ) const {
const auto buffs = FindBuffs ( item , true ) ;
2021-12-05 17:54:36 +00:00
for ( const auto buff : buffs ) {
SkillComponent : : HandleUnmanaged ( buff , m_Parent - > GetObjectID ( ) ) ;
}
}
2022-11-28 00:40:14 +00:00
// TODO Something needs to send the remove buff GameMessage as well when it is unequipping items that would remove buffs.
2022-03-27 22:24:06 +00:00
void InventoryComponent : : RemoveBuff ( Item * item ) const {
const auto buffs = FindBuffs ( item , false ) ;
2021-12-05 17:54:36 +00:00
for ( const auto buff : buffs ) {
SkillComponent : : HandleUnCast ( buff , m_Parent - > GetObjectID ( ) ) ;
}
}
void InventoryComponent : : PushEquippedItems ( ) {
m_Pushed = m_Equipped ;
m_Dirty = true ;
}
void InventoryComponent : : PopEquippedItems ( ) {
auto current = m_Equipped ;
for ( const auto & pair : current ) {
auto * const item = FindItemById ( pair . second . id ) ;
if ( item = = nullptr ) {
continue ;
}
item - > UnEquip ( ) ;
}
for ( const auto & pair : m_Pushed ) {
auto * const item = FindItemById ( pair . second . id ) ;
if ( item = = nullptr ) {
continue ;
}
item - > Equip ( ) ;
}
2022-07-06 08:30:13 +00:00
m_Pushed . clear ( ) ;
auto destroyableComponent = m_Parent - > GetComponent < DestroyableComponent > ( ) ;
2022-07-25 02:26:51 +00:00
2022-07-06 08:30:13 +00:00
// Reset stats to full
if ( destroyableComponent ) {
destroyableComponent - > SetHealth ( static_cast < int32_t > ( destroyableComponent - > GetMaxHealth ( ) ) ) ;
destroyableComponent - > SetArmor ( static_cast < int32_t > ( destroyableComponent - > GetMaxArmor ( ) ) ) ;
destroyableComponent - > SetImagination ( static_cast < int32_t > ( destroyableComponent - > GetMaxImagination ( ) ) ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2022-07-06 08:30:13 +00:00
}
2021-12-05 17:54:36 +00:00
m_Dirty = true ;
}
bool InventoryComponent : : IsEquipped ( const LOT lot ) const {
for ( const auto & pair : m_Equipped ) {
if ( pair . second . lot = = lot ) {
return true ;
}
}
return false ;
}
2022-01-07 02:12:47 +00:00
void InventoryComponent : : CheckItemSet ( const LOT lot ) {
2021-12-05 17:54:36 +00:00
// Check if the lot is in the item set cache
2022-01-07 02:12:47 +00:00
if ( std : : find ( m_ItemSetsChecked . begin ( ) , m_ItemSetsChecked . end ( ) , lot ) ! = m_ItemSetsChecked . end ( ) ) {
2021-12-05 17:54:36 +00:00
return ;
}
2022-01-13 03:48:27 +00:00
const std : : string lot_query = " % " + std : : to_string ( lot ) + " % " ;
auto query = CDClientDatabase : : CreatePreppedStmt (
" SELECT setID FROM ItemSets WHERE itemIDs LIKE ?; " ) ;
query . bind ( 1 , lot_query . c_str ( ) ) ;
auto result = query . execQuery ( ) ;
2021-12-05 17:54:36 +00:00
2022-01-07 02:12:47 +00:00
while ( ! result . eof ( ) ) {
2024-04-14 04:41:51 +00:00
const auto id = result . getIntField ( " setID " ) ;
2021-12-05 17:54:36 +00:00
bool found = false ;
// Check if we have the set already
for ( auto * itemset : m_Itemsets ) {
if ( itemset - > GetID ( ) = = id ) {
found = true ;
break ;
}
}
if ( ! found ) {
auto * set = new ItemSet ( id , this ) ;
m_Itemsets . push_back ( set ) ;
}
result . nextRow ( ) ;
}
m_ItemSetsChecked . push_back ( lot ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
result . finalize ( ) ;
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : SetConsumable ( LOT lot ) {
2021-12-05 17:54:36 +00:00
m_Consumable = lot ;
}
LOT InventoryComponent : : GetConsumable ( ) const {
return m_Consumable ;
}
void InventoryComponent : : AddItemSkills ( const LOT lot ) {
const auto info = Inventory : : FindItemComponent ( lot ) ;
const auto slot = FindBehaviorSlot ( static_cast < eItemType > ( info . itemType ) ) ;
if ( slot = = BehaviorSlot : : Invalid ) {
return ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto index = m_Skills . find ( slot ) ;
const auto skill = FindSkill ( lot ) ;
2023-10-09 20:18:51 +00:00
SetSkill ( slot , skill ) ;
2021-12-05 17:54:36 +00:00
}
2024-12-11 03:10:54 +00:00
void InventoryComponent : : FixInvisibleItems ( ) {
const auto numberItemsLoadedPerFrame = 12.0f ;
const auto callbackTime = 0.125f ;
const auto arbitaryInventorySize = 300.0f ; // max in live + dlu is less than 300, seems like a good number.
auto * const items = GetInventory ( eInventoryType : : ITEMS ) ;
if ( ! items ) return ;
// Add an extra update to make sure the client can see all the items.
const auto something = static_cast < int32_t > ( std : : ceil ( items - > GetItems ( ) . size ( ) / arbitaryInventorySize ) ) + 1 ;
LOG_DEBUG ( " Fixing invisible items with %i updates " , something ) ;
for ( int32_t i = 1 ; i < something + 1 ; i + + ) {
// client loads 12 items every 1/8 seconds, we're adding a small hack to fix invisible inventory items due to closing the news screen too fast.
m_Parent - > AddCallbackTimer ( ( arbitaryInventorySize / numberItemsLoadedPerFrame ) * callbackTime * i , [ this ] ( ) {
GameMessages : : SendUpdateInventoryUi ( m_Parent - > GetObjectID ( ) , m_Parent - > GetSystemAddress ( ) ) ;
} ) ;
}
}
2021-12-05 17:54:36 +00:00
void InventoryComponent : : RemoveItemSkills ( const LOT lot ) {
const auto info = Inventory : : FindItemComponent ( lot ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto slot = FindBehaviorSlot ( static_cast < eItemType > ( info . itemType ) ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( slot = = BehaviorSlot : : Invalid ) {
return ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto index = m_Skills . find ( slot ) ;
if ( index = = m_Skills . end ( ) ) {
return ;
}
const auto old = index - > second ;
GameMessages : : SendRemoveSkill ( m_Parent , old ) ;
m_Skills . erase ( slot ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( slot = = BehaviorSlot : : Primary ) {
m_Skills . insert_or_assign ( BehaviorSlot : : Primary , 1 ) ;
2023-10-09 20:18:51 +00:00
GameMessages : : SendAddSkill ( m_Parent , 1 , BehaviorSlot : : Primary ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-12-24 02:05:30 +00:00
void InventoryComponent : : TriggerPassiveAbility ( PassiveAbilityTrigger trigger , Entity * target ) {
2021-12-05 17:54:36 +00:00
for ( auto * set : m_Itemsets ) {
2022-12-24 02:05:30 +00:00
set - > TriggerPassiveAbility ( trigger , target ) ;
2021-12-05 17:54:36 +00:00
}
}
bool InventoryComponent : : HasAnyPassive ( const std : : vector < eItemSetPassiveAbilityID > & passiveIDs , int32_t equipmentRequirement ) const {
for ( auto * set : m_Itemsets ) {
if ( set - > GetEquippedCount ( ) < equipmentRequirement ) {
continue ;
}
// Check if the set has any of the passive abilities
if ( std : : find ( passiveIDs . begin ( ) , passiveIDs . end ( ) , static_cast < eItemSetPassiveAbilityID > ( set - > GetID ( ) ) ) ! = passiveIDs . end ( ) ) {
return true ;
}
}
return false ;
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : DespawnPet ( ) {
2021-12-05 17:54:36 +00:00
auto * current = PetComponent : : GetActivePet ( m_Parent - > GetObjectID ( ) ) ;
if ( current ! = nullptr ) {
current - > Deactivate ( ) ;
}
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : SpawnPet ( Item * item ) {
2021-12-05 17:54:36 +00:00
auto * current = PetComponent : : GetActivePet ( m_Parent - > GetObjectID ( ) ) ;
if ( current ! = nullptr ) {
current - > Deactivate ( ) ;
if ( current - > GetDatabaseId ( ) = = item - > GetSubKey ( ) ) {
return ;
}
}
2022-06-18 06:53:09 +00:00
// First check if we can summon the pet. You need 1 imagination to do so.
auto destroyableComponent = m_Parent - > GetComponent < DestroyableComponent > ( ) ;
2022-06-18 20:25:34 +00:00
if ( Game : : config - > GetValue ( " pets_take_imagination " ) = = " 1 " & & destroyableComponent & & destroyableComponent - > GetImagination ( ) < = 0 ) {
2023-05-02 22:39:21 +00:00
GameMessages : : SendUseItemRequirementsResponse ( m_Parent - > GetObjectID ( ) , m_Parent - > GetSystemAddress ( ) , eUseItemResponse : : NoImaginationForPet ) ;
2022-06-18 06:53:09 +00:00
return ;
}
2021-12-05 17:54:36 +00:00
EntityInfo info { } ;
info . lot = item - > GetLot ( ) ;
info . pos = m_Parent - > GetPosition ( ) ;
2024-01-29 07:53:12 +00:00
info . rot = NiQuaternionConstant : : IDENTITY ;
2021-12-05 17:54:36 +00:00
info . spawnerID = m_Parent - > GetObjectID ( ) ;
2022-01-13 03:48:27 +00:00
2023-07-15 20:56:33 +00:00
auto * pet = Game : : entityManager - > CreateEntity ( info ) ;
2021-12-05 17:54:36 +00:00
auto * petComponent = pet - > GetComponent < PetComponent > ( ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( petComponent ! = nullptr ) {
petComponent - > Activate ( item ) ;
}
2023-07-15 20:56:33 +00:00
Game : : entityManager - > ConstructEntity ( pet ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : SetDatabasePet ( LWOOBJID id , const DatabasePet & data ) {
2021-12-05 17:54:36 +00:00
m_Pets . insert_or_assign ( id , data ) ;
}
const DatabasePet & InventoryComponent : : GetDatabasePet ( LWOOBJID id ) const {
const auto & pair = m_Pets . find ( id ) ;
if ( pair = = m_Pets . end ( ) ) return DATABASE_PET_INVALID ;
return pair - > second ;
}
bool InventoryComponent : : IsPet ( LWOOBJID id ) const {
const auto & pair = m_Pets . find ( id ) ;
return pair ! = m_Pets . end ( ) ;
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : RemoveDatabasePet ( LWOOBJID id ) {
2021-12-05 17:54:36 +00:00
m_Pets . erase ( id ) ;
}
BehaviorSlot InventoryComponent : : FindBehaviorSlot ( const eItemType type ) {
switch ( type ) {
2022-07-17 01:36:09 +00:00
case eItemType : : HAT :
2021-12-05 17:54:36 +00:00
return BehaviorSlot : : Head ;
2022-07-17 01:36:09 +00:00
case eItemType : : NECK :
2021-12-05 17:54:36 +00:00
return BehaviorSlot : : Neck ;
2022-07-17 01:36:09 +00:00
case eItemType : : LEFT_HAND :
2021-12-05 17:54:36 +00:00
return BehaviorSlot : : Offhand ;
2022-07-17 01:36:09 +00:00
case eItemType : : RIGHT_HAND :
2021-12-05 17:54:36 +00:00
return BehaviorSlot : : Primary ;
2022-07-17 01:36:09 +00:00
case eItemType : : CONSUMABLE :
2021-12-05 17:54:36 +00:00
return BehaviorSlot : : Consumable ;
default :
return BehaviorSlot : : Invalid ;
}
}
2022-01-13 03:48:27 +00:00
bool InventoryComponent : : IsTransferInventory ( eInventoryType type ) {
2022-11-28 00:48:46 +00:00
return type = = VENDOR_BUYBACK | | type = = VAULT_ITEMS | | type = = VAULT_MODELS | | type = = TEMP_ITEMS | | type = = TEMP_MODELS | | type = = MODELS_IN_BBB ;
2021-12-05 17:54:36 +00:00
}
uint32_t InventoryComponent : : FindSkill ( const LOT lot ) {
2024-02-09 05:40:43 +00:00
auto * table = CDClientManager : : GetTable < CDObjectSkillsTable > ( ) ;
2021-12-05 17:54:36 +00:00
const auto results = table - > Query ( [ = ] ( const CDObjectSkills & entry ) {
return entry . objectTemplate = = static_cast < unsigned int > ( lot ) ;
} ) ;
for ( const auto & result : results ) {
if ( result . castOnType = = 0 ) {
return result . skillID ;
}
}
return 0 ;
}
2022-03-27 22:24:06 +00:00
std : : vector < uint32_t > InventoryComponent : : FindBuffs ( Item * item , bool castOnEquip ) const {
std : : vector < uint32_t > buffs ;
if ( item = = nullptr ) return buffs ;
2024-02-09 05:40:43 +00:00
auto * table = CDClientManager : : GetTable < CDObjectSkillsTable > ( ) ;
auto * behaviors = CDClientManager : : GetTable < CDSkillBehaviorTable > ( ) ;
2021-12-05 17:54:36 +00:00
const auto results = table - > Query ( [ = ] ( const CDObjectSkills & entry ) {
2022-03-27 22:24:06 +00:00
return entry . objectTemplate = = static_cast < unsigned int > ( item - > GetLot ( ) ) ;
2021-12-05 17:54:36 +00:00
} ) ;
2023-03-04 07:16:37 +00:00
auto * missions = static_cast < MissionComponent * > ( m_Parent - > GetComponent ( eReplicaComponentType : : MISSION ) ) ;
2021-12-05 17:54:36 +00:00
for ( const auto & result : results ) {
if ( result . castOnType = = 1 ) {
const auto entry = behaviors - > GetSkillByID ( result . skillID ) ;
if ( entry . skillID = = 0 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find buff behavior for skill (%i)! " , result . skillID ) ;
2021-12-05 17:54:36 +00:00
continue ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( missions ! = nullptr & & castOnEquip ) {
missions - > Progress ( eMissionTaskType : : USE_SKILL , result . skillID ) ;
}
2022-07-25 02:26:51 +00:00
2022-03-27 22:24:06 +00:00
// If item is not a proxy, add its buff to the added buffs.
if ( item - > GetParent ( ) = = LWOOBJID_EMPTY ) buffs . push_back ( static_cast < uint32_t > ( entry . behaviorID ) ) ;
2021-12-05 17:54:36 +00:00
}
}
return buffs ;
}
2022-01-13 03:48:27 +00:00
void InventoryComponent : : SetNPCItems ( const std : : vector < LOT > & items ) {
2021-12-05 17:54:36 +00:00
m_Equipped . clear ( ) ;
auto slot = 0u ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( const auto & item : items ) {
2024-01-05 12:31:22 +00:00
const LWOOBJID id = ObjectIDManager : : GenerateObjectID ( ) ;
2021-12-05 17:54:36 +00:00
const auto & info = Inventory : : FindItemComponent ( item ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
UpdateSlot ( info . equipLocation , { id , static_cast < LOT > ( item ) , 1 , slot + + } , true ) ;
}
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
InventoryComponent : : ~ InventoryComponent ( ) {
for ( const auto & inventory : m_Inventories ) {
delete inventory . second ;
}
m_Inventories . clear ( ) ;
for ( auto * set : m_Itemsets ) {
delete set ;
}
m_Itemsets . clear ( ) ;
m_Pets . clear ( ) ;
}
std : : vector < Item * > InventoryComponent : : GenerateProxies ( Item * parent ) {
std : : vector < Item * > proxies ;
auto subItems = parent - > GetInfo ( ) . subItems ;
if ( subItems . empty ( ) ) {
return proxies ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
subItems . erase ( std : : remove_if ( subItems . begin ( ) , subItems . end ( ) , : : isspace ) , subItems . end ( ) ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
std : : stringstream stream ( subItems ) ;
std : : string segment ;
std : : vector < uint32_t > lots ;
while ( std : : getline ( stream , segment , ' , ' ) ) {
try {
lots . push_back ( std : : stoi ( segment ) ) ;
} catch ( std : : invalid_argument & exception ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to parse proxy (%s): (%s)! " , segment . c_str ( ) , exception . what ( ) ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-13 03:48:27 +00:00
}
2021-12-05 17:54:36 +00:00
for ( const auto lot : lots ) {
if ( ! Inventory : : IsValidItem ( lot ) ) {
continue ;
}
auto * inventory = GetInventory ( ITEM_SETS ) ;
auto * proxy = new Item ( lot , inventory , inventory - > FindEmptySlot ( ) , 1 , { } , parent - > GetId ( ) , false ) ;
EquipItem ( proxy ) ;
proxies . push_back ( proxy ) ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
return proxies ;
}
std : : vector < Item * > InventoryComponent : : FindProxies ( const LWOOBJID parent ) {
auto * inventory = GetInventory ( ITEM_SETS ) ;
std : : vector < Item * > proxies ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( const auto & pair : inventory - > GetItems ( ) ) {
auto * item = pair . second ;
if ( item - > GetParent ( ) = = parent ) {
proxies . push_back ( item ) ;
}
}
return proxies ;
}
bool InventoryComponent : : IsValidProxy ( const LWOOBJID parent ) {
for ( const auto & pair : m_Inventories ) {
const auto items = pair . second - > GetItems ( ) ;
for ( const auto & candidate : items ) {
auto * item = candidate . second ;
if ( item - > GetId ( ) = = parent ) {
return true ;
}
}
}
return false ;
}
bool InventoryComponent : : IsParentValid ( Item * root ) {
if ( root - > GetInfo ( ) . subItems . empty ( ) ) {
return true ;
}
const auto id = root - > GetId ( ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( const auto & pair : m_Inventories ) {
const auto items = pair . second - > GetItems ( ) ;
for ( const auto & candidate : items ) {
auto * item = candidate . second ;
if ( item - > GetParent ( ) = = id ) {
return true ;
}
}
}
return false ;
}
void InventoryComponent : : CheckProxyIntegrity ( ) {
std : : vector < Item * > dead ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
for ( const auto & pair : m_Inventories ) {
const auto & items = pair . second - > GetItems ( ) ;
for ( const auto & candidate : items ) {
auto * item = candidate . second ;
const auto parent = item - > GetParent ( ) ;
if ( parent = = LWOOBJID_EMPTY ) {
continue ;
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( IsValidProxy ( parent ) ) {
continue ;
}
dead . push_back ( item ) ;
}
}
for ( auto * item : dead ) {
item - > RemoveFromInventory ( ) ;
}
dead . clear ( ) ;
/*
for ( const auto & pair : inventories )
{
const auto & items = pair . second - > GetItems ( ) ;
for ( const auto & candidate : items )
{
auto * item = candidate . second ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
const auto parent = item - > GetParent ( ) ;
if ( parent ! = LWOOBJID_EMPTY )
{
continue ;
}
if ( ! item - > IsEquipped ( ) )
{
continue ;
}
if ( IsParentValid ( item ) )
{
continue ;
}
dead . push_back ( item ) ;
}
}
for ( auto * item : dead )
{
item - > RemoveFromInventory ( ) ;
}
*/
}
void InventoryComponent : : PurgeProxies ( Item * item ) {
const auto root = item - > GetParent ( ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
if ( root ! = LWOOBJID_EMPTY ) {
2024-05-31 04:53:03 +00:00
Item * itemRoot = FindItemById ( root ) ;
2021-12-05 17:54:36 +00:00
2024-05-31 04:53:03 +00:00
if ( itemRoot ! = nullptr ) {
UnEquipItem ( itemRoot ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
return ;
}
auto proxies = FindProxies ( item - > GetId ( ) ) ;
for ( auto * proxy : proxies ) {
proxy - > UnEquip ( ) ;
proxy - > RemoveFromInventory ( ) ;
}
}
2024-04-08 20:13:49 +00:00
void InventoryComponent : : LoadPetXml ( const tinyxml2 : : XMLDocument & document ) {
auto * petInventoryElement = document . FirstChildElement ( " obj " ) - > FirstChildElement ( " pet " ) ;
2021-12-05 17:54:36 +00:00
if ( petInventoryElement = = nullptr ) {
m_Pets . clear ( ) ;
return ;
}
auto * petElement = petInventoryElement - > FirstChildElement ( ) ;
while ( petElement ! = nullptr ) {
LWOOBJID id ;
LOT lot ;
int32_t moderationStatus ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
petElement - > QueryAttribute ( " id " , & id ) ;
petElement - > QueryAttribute ( " l " , & lot ) ;
petElement - > QueryAttribute ( " m " , & moderationStatus ) ;
const char * name = petElement - > Attribute ( " n " ) ;
DatabasePet databasePet ;
databasePet . lot = lot ;
databasePet . moderationState = moderationStatus ;
databasePet . name = std : : string ( name ) ;
SetDatabasePet ( id , databasePet ) ;
petElement = petElement - > NextSiblingElement ( ) ;
}
}
2024-04-08 20:13:49 +00:00
void InventoryComponent : : UpdatePetXml ( tinyxml2 : : XMLDocument & document ) {
auto * petInventoryElement = document . FirstChildElement ( " obj " ) - > FirstChildElement ( " pet " ) ;
2021-12-05 17:54:36 +00:00
if ( petInventoryElement = = nullptr ) {
2024-04-08 20:13:49 +00:00
petInventoryElement = document . NewElement ( " pet " ) ;
2021-12-05 17:54:36 +00:00
2024-04-08 20:13:49 +00:00
document . FirstChildElement ( " obj " ) - > LinkEndChild ( petInventoryElement ) ;
2021-12-05 17:54:36 +00:00
}
petInventoryElement - > DeleteChildren ( ) ;
for ( const auto & pet : m_Pets ) {
2024-04-08 20:13:49 +00:00
auto * petElement = document . NewElement ( " p " ) ;
2021-12-05 17:54:36 +00:00
petElement - > SetAttribute ( " id " , pet . first ) ;
petElement - > SetAttribute ( " l " , pet . second . lot ) ;
petElement - > SetAttribute ( " m " , pet . second . moderationState ) ;
petElement - > SetAttribute ( " n " , pet . second . name . c_str ( ) ) ;
petElement - > SetAttribute ( " t " , 0 ) ;
2022-01-13 03:48:27 +00:00
2021-12-05 17:54:36 +00:00
petInventoryElement - > LinkEndChild ( petElement ) ;
}
}
2023-10-09 20:18:51 +00:00
2024-05-23 00:06:52 +00:00
bool InventoryComponent : : SetSkill ( int32_t slot , uint32_t skillId ) {
2023-10-09 20:18:51 +00:00
BehaviorSlot behaviorSlot = BehaviorSlot : : Invalid ;
2024-05-23 00:06:52 +00:00
if ( slot = = 1 ) behaviorSlot = BehaviorSlot : : Primary ;
else if ( slot = = 2 ) behaviorSlot = BehaviorSlot : : Offhand ;
else if ( slot = = 3 ) behaviorSlot = BehaviorSlot : : Neck ;
else if ( slot = = 4 ) behaviorSlot = BehaviorSlot : : Head ;
else if ( slot = = 5 ) behaviorSlot = BehaviorSlot : : Consumable ;
2023-10-09 20:18:51 +00:00
else return false ;
return SetSkill ( behaviorSlot , skillId ) ;
}
2024-05-23 00:06:52 +00:00
bool InventoryComponent : : SetSkill ( BehaviorSlot slot , uint32_t skillId ) {
2023-10-09 20:18:51 +00:00
if ( skillId = = 0 ) return false ;
const auto index = m_Skills . find ( slot ) ;
if ( index ! = m_Skills . end ( ) ) {
const auto old = index - > second ;
GameMessages : : SendRemoveSkill ( m_Parent , old ) ;
}
GameMessages : : SendAddSkill ( m_Parent , skillId , slot ) ;
m_Skills . insert_or_assign ( slot , skillId ) ;
return true ;
}
2024-08-02 06:38:21 +00:00
void InventoryComponent : : UpdateGroup ( const GroupUpdate & groupUpdate ) {
if ( groupUpdate . groupId . empty ( ) ) return ;
if ( groupUpdate . inventory ! = eInventoryType : : BRICKS & & groupUpdate . inventory ! = eInventoryType : : MODELS ) {
LOG ( " Invalid inventory type for grouping %s " , StringifiedEnum : : ToString ( groupUpdate . inventory ) . data ( ) ) ;
return ;
}
auto & groups = m_Groups [ groupUpdate . inventory ] ;
auto groupItr = std : : ranges : : find_if ( groups , [ & groupUpdate ] ( const Group & group ) {
return group . groupId = = groupUpdate . groupId ;
} ) ;
if ( groupUpdate . command ! = GroupUpdateCommand : : ADD & & groupItr = = groups . end ( ) ) {
LOG ( " Group %s not found in inventory %s. Cannot process command. " , groupUpdate . groupId . c_str ( ) , StringifiedEnum : : ToString ( groupUpdate . inventory ) . data ( ) ) ;
return ;
}
if ( groupUpdate . command = = GroupUpdateCommand : : ADD & & groups . size ( ) > = MaximumGroupCount ) {
LOG ( " Cannot add group to inventory %s. Maximum group count reached. " , StringifiedEnum : : ToString ( groupUpdate . inventory ) . data ( ) ) ;
return ;
}
switch ( groupUpdate . command ) {
case GroupUpdateCommand : : ADD : {
auto & group = groups . emplace_back ( ) ;
group . groupId = groupUpdate . groupId ;
group . groupName = groupUpdate . groupName ;
break ;
}
case GroupUpdateCommand : : ADD_LOT : {
groupItr - > lots . insert ( groupUpdate . lot ) ;
break ;
}
case GroupUpdateCommand : : REMOVE : {
groups . erase ( groupItr ) ;
break ;
}
case GroupUpdateCommand : : REMOVE_LOT : {
groupItr - > lots . erase ( groupUpdate . lot ) ;
break ;
}
case GroupUpdateCommand : : MODIFY : {
groupItr - > groupName = groupUpdate . groupName ;
break ;
}
default : {
LOG ( " Invalid group update command %i " , groupUpdate . command ) ;
break ;
}
}
}
void InventoryComponent : : UpdateGroupXml ( tinyxml2 : : XMLElement & groups ) const {
for ( const auto & [ inventory , groupsData ] : m_Groups ) {
for ( const auto & group : groupsData ) {
auto * const groupElement = groups . InsertNewChildElement ( " grp " ) ;
groupElement - > SetAttribute ( " id " , group . groupId . c_str ( ) ) ;
groupElement - > SetAttribute ( " n " , group . groupName . c_str ( ) ) ;
groupElement - > SetAttribute ( " t " , static_cast < uint32_t > ( inventory ) ) ;
groupElement - > SetAttribute ( " u " , 0 ) ;
std : : ostringstream lots ;
bool first = true ;
for ( const auto lot : group . lots ) {
if ( ! first ) lots < < ' ' ;
first = false ;
lots < < lot ;
}
groupElement - > SetAttribute ( " l " , lots . str ( ) . c_str ( ) ) ;
}
}
}
void InventoryComponent : : LoadGroupXml ( const tinyxml2 : : XMLElement & groups ) {
auto * groupElement = groups . FirstChildElement ( " grp " ) ;
while ( groupElement ) {
const char * groupId = nullptr ;
const char * groupName = nullptr ;
const char * lots = nullptr ;
uint32_t inventory = eInventoryType : : INVALID ;
groupElement - > QueryStringAttribute ( " id " , & groupId ) ;
groupElement - > QueryStringAttribute ( " n " , & groupName ) ;
groupElement - > QueryStringAttribute ( " l " , & lots ) ;
groupElement - > QueryAttribute ( " t " , & inventory ) ;
if ( ! groupId | | ! groupName | | ! lots ) {
LOG ( " Failed to load group from xml id %i name %i lots %i " ,
groupId = = nullptr , groupName = = nullptr , lots = = nullptr ) ;
} else {
auto & group = m_Groups [ static_cast < eInventoryType > ( inventory ) ] . emplace_back ( ) ;
group . groupId = groupId ;
group . groupName = groupName ;
for ( const auto & lotStr : GeneralUtils : : SplitString ( lots , ' ' ) ) {
auto lot = GeneralUtils : : TryParse < LOT > ( lotStr ) ;
if ( lot ) group . lots . insert ( * lot ) ;
}
}
groupElement = groupElement - > NextSiblingElement ( " grp " ) ;
}
}