diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 5cce81d3..170c23e3 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -216,7 +216,8 @@ enum ActorControlType : uint16_t BuildPresetResponse = 0x3ED, /*! - * param1 = object array index + * param1 = u16 landid + * u16 slotid */ RemoveExteriorHousingItem = 0x3EF, diff --git a/src/common/Network/PacketDef/Zone/ClientZoneDef.h b/src/common/Network/PacketDef/Zone/ClientZoneDef.h index 87867f0a..4af1d691 100644 --- a/src/common/Network/PacketDef/Zone/ClientZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ClientZoneDef.h @@ -41,7 +41,7 @@ struct FFXIVIpcClientTrigger : /* 0004 */ uint32_t param11; /* 0008 */ uint32_t param12; /* 000C */ uint32_t param2; - /* 0010 */ uint32_t housingParam; // todo: param4? + /* 0010 */ uint32_t param4; // todo: really? /* 0014 */ char unk_14[4]; /* 0018 */ uint64_t param3; }; diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 3a59f726..2289d197 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -365,9 +365,7 @@ namespace Sapphire::Entity Common::GearModelSlot equipSlotToModelSlot( Common::GearSetSlot slot ); - using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >; - - bool getFreeInventoryContainerSlot( Entity::Player::InventoryContainerPair& containerPair ) const; + bool getFreeInventoryContainerSlot( Inventory::InventoryContainerPair& containerPair ) const; void insertInventoryItem( Common::InventoryType type, uint16_t slot, const Sapphire::ItemPtr item ); diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index 95bd52fb..21547da4 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -903,7 +903,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common: return item; } -bool Sapphire::Entity::Player::getFreeInventoryContainerSlot( Entity::Player::InventoryContainerPair& containerPair ) const +bool Sapphire::Entity::Player::getFreeInventoryContainerSlot( Inventory::InventoryContainerPair& containerPair ) const { for( auto bagId : { Bag0, Bag1, Bag2, Bag3 } ) { diff --git a/src/world/ForwardsZone.h b/src/world/ForwardsZone.h index 82ad0e04..6d689826 100644 --- a/src/world/ForwardsZone.h +++ b/src/world/ForwardsZone.h @@ -37,6 +37,8 @@ TYPE_FORWARD( HousingInteriorTerritory ); namespace Inventory { +using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >; +using InventoryTypeList = std::vector< Common::InventoryType >; TYPE_FORWARD( HousingItem ); } diff --git a/src/world/Inventory/HousingItem.cpp b/src/world/Inventory/HousingItem.cpp index 265c26de..b0134125 100644 --- a/src/world/Inventory/HousingItem.cpp +++ b/src/world/Inventory/HousingItem.cpp @@ -2,7 +2,9 @@ Sapphire::Inventory::HousingItem::HousingItem( uint64_t uId, uint32_t catalogId ) : Sapphire::Item( uId, catalogId, false ) -{ } +{ + m_stackSize = 1; +} uint16_t Sapphire::Inventory::HousingItem::getRot() const { diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index 7e576b4e..ef3e68c9 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -1092,7 +1092,6 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl invMgr->saveItem( player, item ); // resend container - // todo: unsure as to whether we need to resend every container or just the one we edit - we'll see how this goes invMgr->sendInventoryContainer( player, container ); invMgr->saveHousingContainer( ident, container ); invMgr->updateHousingItemPosition( item ); @@ -1128,7 +1127,7 @@ void Sapphire::World::Manager::HousingMgr::sendInternalEstateInventoryBatch( Sap // todo: perms check - InventoryTypeList containerIds; + Inventory::InventoryTypeList containerIds; if( storeroom ) containerIds = m_internalStoreroomContainers; @@ -1297,7 +1296,7 @@ void Sapphire::World::Manager::HousingMgr::reqRemoveHousingItem( Sapphire::Entit if( land->getOwnerId() != player.getId() ) return; - removeExternalItem( player, *terri, containerId, slot, sendToStoreroom ); + removeExternalItem( player, *terri, *land, slot, sendToStoreroom ); } } @@ -1336,18 +1335,15 @@ bool Sapphire::World::Manager::HousingMgr::removeInternalItem( Entity::Player& p if( !item ) return false; - item->setStackSize( 1 ); - if( !sendToStoreroom ) { // make sure the player has a free inv slot first - Entity::Player::InventoryContainerPair containerPair; + Inventory::InventoryContainerPair containerPair; if( !player.getFreeInventoryContainerSlot( containerPair ) ) return false; auto invMgr = g_fw.get< InventoryMgr >(); - // remove it from housing inventory container->removeItem( slotId ); invMgr->sendInventoryContainer( player, container ); @@ -1358,20 +1354,108 @@ bool Sapphire::World::Manager::HousingMgr::removeInternalItem( Entity::Player& p player.insertInventoryItem( containerPair.first, containerPair.second, item ); // todo: set item as bound/unsellable/untradable - - // despawn - auto arraySlot = ( containerIdx * 50 ) + slotId; - - terri.removeHousingObject( arraySlot ); } + else + { + ItemContainerPtr freeContainer; + Inventory::InventoryContainerPair freeSlotPair; + freeContainer = getFreeEstateInventorySlot( terri.getLandIdent(), freeSlotPair, m_internalStoreroomContainers ); + + if( !freeContainer ) + return false; + + auto invMgr = g_fw.get< InventoryMgr >(); + + container->removeItem( slotId ); + invMgr->sendInventoryContainer( player, container ); + invMgr->removeHousingItemPosition( *item ); + invMgr->removeItemFromHousingContainer( terri.getLandIdent(), containerId, slotId ); + + freeContainer->setItem( slotId, item ); + invMgr->sendInventoryContainer( player, freeContainer ); + invMgr->saveHousingContainer( terri.getLandIdent(), freeContainer ); + } + + // despawn + auto arraySlot = ( containerIdx * 50 ) + slotId; + terri.removeHousingObject( arraySlot ); return true; } -bool Sapphire::World::Manager::HousingMgr::removeExternalItem( Entity::Player& player, - HousingZone& terri, - uint16_t containerId, uint16_t slotId, - bool sendToStoreroom ) +bool Sapphire::World::Manager::HousingMgr::removeExternalItem( Entity::Player& player, HousingZone& terri, Land& land, + uint16_t slotId, bool sendToStoreroom ) { + auto& containers = getEstateInventory( land.getLandIdent() ); + auto& placedContainer = containers[ InventoryType::HousingExteriorPlacedItems ]; + + auto item = std::dynamic_pointer_cast< Inventory::HousingItem >( placedContainer->getItem( slotId ) ); + if( !item ) + return false; + + auto invMgr = g_fw.get< InventoryMgr >(); + + if( sendToStoreroom ) + { + auto& storeroomContainer = containers[ InventoryType::HousingExteriorStoreroom ]; + auto freeSlot = storeroomContainer->getFreeSlot(); + + if( freeSlot == -1 ) + return false; + + placedContainer->removeItem( slotId ); + invMgr->sendInventoryContainer( player, placedContainer ); + invMgr->removeHousingItemPosition( *item ); + invMgr->removeItemFromHousingContainer( land.getLandIdent(), placedContainer->getId(), slotId ); + + storeroomContainer->setItem( freeSlot, item ); + invMgr->sendInventoryContainer( player, storeroomContainer ); + invMgr->saveHousingContainer( land.getLandIdent(), storeroomContainer ); + } + else + { + Inventory::InventoryContainerPair containerPair; + if( !player.getFreeInventoryContainerSlot( containerPair ) ) + return false; + + // remove from housing inv + placedContainer->removeItem( slotId ); + invMgr->sendInventoryContainer( player, placedContainer ); + invMgr->removeHousingItemPosition( *item ); + invMgr->removeItemFromHousingContainer( land.getLandIdent(), placedContainer->getId(), slotId ); + + // add to player inv + player.insertInventoryItem( containerPair.first, containerPair.second, item ); + } + + terri.despawnYardObject( land.getLandIdent().landId, slotId ); + + return true; +} + +Sapphire::ItemContainerPtr Sapphire::World::Manager::HousingMgr::getFreeEstateInventorySlot( Common::LandIdent ident, + Inventory::InventoryContainerPair& pair, + Inventory::InventoryTypeList bagList ) +{ + auto& estateContainers = getEstateInventory( ident ); + + for( auto bag : bagList ) + { + auto needle = estateContainers.find( bag ); + if( needle == estateContainers.end() ) + continue; + + auto container = needle->second; + + auto freeSlot = container->getFreeSlot(); + + if( freeSlot == -1 ) + continue; + + pair = std::make_pair( bag, freeSlot ); + return container; + } + + return nullptr; } \ No newline at end of file diff --git a/src/world/Manager/HousingMgr.h b/src/world/Manager/HousingMgr.h index 53c42362..804420e2 100644 --- a/src/world/Manager/HousingMgr.h +++ b/src/world/Manager/HousingMgr.h @@ -66,8 +66,6 @@ namespace Sapphire::World::Manager */ using LandIdentToInventoryContainerMap = std::unordered_map< uint64_t, ContainerIdToContainerMap >; - using InventoryTypeList = std::vector< Common::InventoryType >; - HousingMgr(); virtual ~HousingMgr(); @@ -176,6 +174,10 @@ namespace Sapphire::World::Manager private: + ItemContainerPtr getFreeEstateInventorySlot( Common::LandIdent ident, + Inventory::InventoryContainerPair& pair, + Inventory::InventoryTypeList bagList ); + /*! * * @param player @@ -192,13 +194,12 @@ namespace Sapphire::World::Manager * * @param player * @param terri - * @param containerId * @param slotId * @param sendToStoreroom * @return */ - bool removeExternalItem( Entity::Player& player, HousingZone& terri, - uint16_t containerId, uint16_t slotId, bool sendToStoreroom ); + bool removeExternalItem( Entity::Player& player, HousingZone& terri, Land& land, + uint16_t slotId, bool sendToStoreroom ); /*! * @brief Processes the movement of an item placed in a HousingZone @@ -296,8 +297,8 @@ namespace Sapphire::World::Manager LandSetLandCacheMap m_landCache; LandIdentToInventoryContainerMap m_estateInventories; - InventoryTypeList m_internalPlacedItemContainers; - InventoryTypeList m_internalStoreroomContainers; + Inventory::InventoryTypeList m_internalPlacedItemContainers; + Inventory::InventoryTypeList m_internalStoreroomContainers; std::array< std::pair< Common::InventoryType, Common::InventoryType >, 8 > m_containerMap; diff --git a/src/world/Network/Handlers/ClientTriggerHandler.cpp b/src/world/Network/Handlers/ClientTriggerHandler.cpp index 18841020..5c9cee52 100644 --- a/src/world/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/world/Network/Handlers/ClientTriggerHandler.cpp @@ -79,14 +79,14 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX const auto param12 = packet.data().param12; const auto param2 = packet.data().param2; const auto param3 = packet.data().param3; - const auto housingParam = packet.data().housingParam; + const auto param4 = packet.data().param4; pLog->debug( "[" + std::to_string( m_pSession->getId() ) + "] Incoming action: " + Util::intToHexString( static_cast< uint32_t >( commandId & 0xFFFF ), 4 ) + "\nparam1: " + Util::intToHexString( static_cast< uint64_t >( param1 & 0xFFFFFFFFFFFFFFF ), 16 ) + "\nparam2: " + Util::intToHexString( static_cast< uint32_t >( param2 & 0xFFFFFFFF ), 8 ) + "\nparam3: " + Util::intToHexString( static_cast< uint64_t >( param3 & 0xFFFFFFFFFFFFFFF ), 16 ) + - "\nhousingParam: " + Util::intToHexString( static_cast< uint32_t >( housingParam & 0xFFFFFFFF ), 8 ) + "\nparam4: " + Util::intToHexString( static_cast< uint32_t >( param4 & 0xFFFFFFFF ), 8 ) ); @@ -460,8 +460,8 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX { auto housingMgr = g_fw.get< HousingMgr >(); - auto slot = housingParam & 0xFF; - auto sendToStoreroom = ( housingParam >> 16 ) != 0; + auto slot = param4 & 0xFF; + auto sendToStoreroom = ( param4 >> 16 ) != 0; //player, plot, containerId, slot, sendToStoreroom housingMgr->reqRemoveHousingItem( player, param12, param2, slot, sendToStoreroom ); diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index 2aaa3b90..829f5ddf 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "Actor/Player.h" #include "Actor/Actor.h" @@ -402,4 +404,21 @@ void Sapphire::HousingZone::updateYardObjectPos( Entity::Player& sourcePlayer, u player.second->queuePacket( packet ); } +} + +void Sapphire::HousingZone::despawnYardObject( uint16_t landId, uint16_t slot ) +{ + auto bounds = m_yardObjectArrayBounds[ landId ]; + auto offset = bounds.first + slot; + auto yardMapIndex = landId <= 29 ? 0 : 1; + + memset( &m_yardObjects[ yardMapIndex ][ offset ], 0x00, sizeof( Common::HousingObject ) ); + + for( const auto& player : m_playerMap ) + { + auto param = ( landId << 16 ) | slot; + auto pkt = Server::makeActorControl143( player.second->getId(), Network::ActorControl::RemoveExteriorHousingItem, param ); + + player.second->queuePacket( pkt ); + } } \ No newline at end of file diff --git a/src/world/Territory/HousingZone.h b/src/world/Territory/HousingZone.h index aac75ca0..8cfcef73 100644 --- a/src/world/Territory/HousingZone.h +++ b/src/world/Territory/HousingZone.h @@ -58,6 +58,7 @@ namespace Sapphire void spawnYardObject( uint8_t landId, uint16_t slotId, Sapphire::Inventory::HousingItem& item ); void updateYardObjectPos( Entity::Player& sourcePlayer, uint16_t slot, uint16_t landId, Inventory::HousingItem& item ); + void despawnYardObject( uint16_t landId, uint16_t slot ); private: using LandPtrMap = std::unordered_map< uint8_t, Sapphire::LandPtr >;