diff --git a/src/common/Common.h b/src/common/Common.h index 8a86bde6..d777a9e2 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -851,7 +851,7 @@ namespace Sapphire::Common uint32_t unkown1; //12 }; - struct YardObject + struct HousingObject { uint32_t itemId; uint16_t itemRotation; diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 8d6e9ea1..6873140c 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -215,6 +215,11 @@ enum ActorControlType : uint16_t ShowBuildPresetUI = 0x3E9, BuildPresetResponse = 0x3ED, + /*! + * param1 = object array index + */ + RemoveInteriorHousingItem = 0x3F1, + /*! * param1 = identity shit * u16 1 - container id @@ -223,6 +228,7 @@ enum ActorControlType : uint16_t * u16 1 - slot */ HousingItemMoveConfirm = 0x3F9, + OpenEstateSettingsUI = 0x3FF, /*! * param1 = outdoor furnishings diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 2f039202..5a406ee8 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1679,22 +1679,20 @@ struct FFXIVIpcLandSetInitialize : FFXIVIpcBasePacket< LandSetInitialize > LandStruct land[ 30 ]; }; -struct FFXIVIpcYardObjectSpawn : FFXIVIpcBasePacket +struct FFXIVIpcYardObjectSpawn : FFXIVIpcBasePacket< YardObjectSpawn > { uint8_t landId; uint8_t objectArray; uint16_t unknown1; - Common::YardObject object; + Common::HousingObject object; }; -struct FFXIVIpcYardObjectMove : FFXIVIpcBasePacket +struct FFXIVIpcYardObjectMove : FFXIVIpcBasePacket< YardObjectMove > { uint16_t itemRotation; uint8_t objectArray; - uint8_t landSetId; - uint16_t pos_x; - uint16_t pos_y; - uint16_t pos_z; + uint8_t landId; + Common::FFXIVARR_POSITION3_U16 pos; uint16_t unknown1; uint16_t unknown2; uint16_t unknown3; @@ -1707,7 +1705,7 @@ struct FFXIVIpcHousingObjectInitialize : FFXIVIpcBasePacket< HousingObjectInitia uint8_t packetNum; uint8_t packetTotal; uint8_t u2; //Outdoor 0 / Indoor 100(?) - Common::YardObject object[100]; + Common::HousingObject object[100]; uint32_t unknown4; //unused }; diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index 3713c83f..aea73e84 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -1010,16 +1010,17 @@ void Sapphire::World::Manager::HousingMgr::reqPlaceHousingItem( Sapphire::Entity auto ident = land->getLandIdent(); + bool status = false; + if( isOutside ) - { - if( !placeExternalItem( player, item, ident ) ) - player.sendUrgent( "An internal error occurred when placing the item." ); - } + status = placeExternalItem( player, item, ident ); else - { - if( !placeInteriorItem( player, item ) ) - player.sendUrgent( "An internal error occurred when placing the item." ); - } + status = placeInteriorItem( player, item ); + + if( status ) + player.queuePacket( Server::makeActorControl143( player.getId(), 0x3f3 ) ); + else + player.sendUrgent( "An internal error occurred when placing the item." ); } bool Sapphire::World::Manager::HousingMgr::placeExternalItem( Entity::Player& player, @@ -1051,7 +1052,7 @@ bool Sapphire::World::Manager::HousingMgr::placeExternalItem( Entity::Player& pl auto zone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() ); assert( zone ); - zone->spawnYardObject( ident.landId, freeSlot, item ); + zone->spawnYardObject( ident.landId, freeSlot, *item ); return true; } @@ -1099,7 +1100,7 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl auto zone = std::dynamic_pointer_cast< Territory::Housing::HousingInteriorTerritory >( player.getCurrentZone() ); assert( zone ); - zone->spawnYardObject( containerIdx, freeSlot, containerId, item ); + zone->spawnHousingObject( containerIdx, freeSlot, containerId, item ); return true; } @@ -1107,9 +1108,9 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl return false; } -Sapphire::Common::YardObject Sapphire::World::Manager::HousingMgr::getYardObjectForItem( Inventory::HousingItemPtr item ) const +Sapphire::Common::HousingObject Sapphire::World::Manager::HousingMgr::getYardObjectForItem( Inventory::HousingItemPtr item ) const { - Sapphire::Common::YardObject obj {}; + Sapphire::Common::HousingObject obj {}; obj.pos = item->getPos(); obj.itemRotation = item->getRot(); @@ -1169,7 +1170,7 @@ void Sapphire::World::Manager::HousingMgr::reqMoveHousingItem( Entity::Player& p } else if( auto terri = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() ) ) { - moveExternalItem( player, ident, slot, pos, rot ); + moveExternalItem( player, ident, slot, *terri, pos, rot ); } } @@ -1214,7 +1215,7 @@ bool Sapphire::World::Manager::HousingMgr::moveInternalItem( Entity::Player& pla auto invMgr = g_fw.get< InventoryMgr >(); invMgr->updateHousingItemPosition( item ); - terri.updateObjectPosition( slot, item->getPos(), item->getRot() ); + terri.updateHousingObjectPosition( slot, item->getPos(), item->getRot() ); // send confirmation to player uint32_t param1 = ( ident.landId << 16 ) | containerId; @@ -1228,8 +1229,42 @@ bool Sapphire::World::Manager::HousingMgr::moveInternalItem( Entity::Player& pla bool Sapphire::World::Manager::HousingMgr::moveExternalItem( Entity::Player& player, Common::LandIdent ident, uint16_t slot, - Common::FFXIVARR_POSITION3 pos, float rot ) + Sapphire::HousingZone& terri, Common::FFXIVARR_POSITION3 pos, + float rot ) { + auto land = terri.getLand( ident.landId ); + + // todo: add proper perms check + if( land->getOwnerId() != player.getId() ) + return false; + + auto& containers = getEstateInventory( ident ); + auto needle = containers.find( InventoryType::HousingExteriorPlacedItems ); + if( needle == containers.end() ) + return false; + + auto container = needle->second; + + auto item = std::dynamic_pointer_cast< Inventory::HousingItem >( container->getItem( slot ) ); + if( !item ) + return false; + + item->setPos( { + Util::floatToUInt16( pos.x ), + Util::floatToUInt16( pos.y ), + Util::floatToUInt16( pos.z ) + } ); + + item->setRot( Util::floatToUInt16Rot( rot ) ); + + auto invMgr = g_fw.get< InventoryMgr >(); + invMgr->updateHousingItemPosition( item ); + + terri.updateYardObjectPos( slot, ident.landId, *item ); + + // todo: something is sent to the player here to indicate the move has finished successfully + // currently they can move one item and then can't move any more + return true; } \ No newline at end of file diff --git a/src/world/Manager/HousingMgr.h b/src/world/Manager/HousingMgr.h index 9fe6dd42..5fc3dc56 100644 --- a/src/world/Manager/HousingMgr.h +++ b/src/world/Manager/HousingMgr.h @@ -163,7 +163,7 @@ namespace Sapphire::World::Manager * @param item The item to convert into a YardObject * @return The resultant YardObject */ - Common::YardObject getYardObjectForItem( Inventory::HousingItemPtr item ) const; + Common::HousingObject getYardObjectForItem( Inventory::HousingItemPtr item ) const; void reqMoveHousingItem( Entity::Player& player, Common::LandIdent ident, uint16_t slot, @@ -186,7 +186,7 @@ namespace Sapphire::World::Manager * @return */ bool moveExternalItem( Entity::Player& player, Common::LandIdent ident, uint16_t slot, - Common::FFXIVARR_POSITION3 pos, float rot ); + Sapphire::HousingZone& terri, Common::FFXIVARR_POSITION3 pos, float rot ); /*! * @brief Processes the movement of an item placed inside a HousingInteriorTerritory diff --git a/src/world/Territory/Housing/HousingInteriorTerritory.cpp b/src/world/Territory/Housing/HousingInteriorTerritory.cpp index 60e147c7..064ba620 100644 --- a/src/world/Territory/Housing/HousingInteriorTerritory.cpp +++ b/src/world/Territory/Housing/HousingInteriorTerritory.cpp @@ -45,7 +45,7 @@ Housing::HousingInteriorTerritory::~HousingInteriorTerritory() = default; bool Sapphire::World::Territory::Housing::HousingInteriorTerritory::init() { - updateYardObjects(); + updateHousingObjects(); return true; } @@ -86,8 +86,8 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onPlayerZone objectInitPacket->data().packetNum = yardPacketNum; objectInitPacket->data().packetTotal = yardPacketTotal; - auto yardObjectSize = sizeof( Common::YardObject ); - memcpy( &objectInitPacket->data().object, m_yardObjects.data() + ( yardPacketNum * 100 ), yardObjectSize * 100 ); + auto yardObjectSize = sizeof( Common::HousingObject ); + memcpy( &objectInitPacket->data().object, m_housingObjects.data() + ( yardPacketNum * 100 ), yardObjectSize * 100 ); player.queuePacket( objectInitPacket ); } @@ -110,7 +110,7 @@ const Common::LandIdent Sapphire::World::Territory::Housing::HousingInteriorTerr return m_landIdent; } -void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateYardObjects() +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateHousingObjects() { auto housingMgr = g_fw.get< Manager::HousingMgr >(); @@ -128,9 +128,9 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateYardOb // zero out the array // there's some really weird behaviour where *some* values will cause the linkshell invite notification to pop up // for some reason - Common::YardObject obj {}; - memset( &obj, 0x0, sizeof( Common::YardObject ) ); - m_yardObjects.fill( obj ); + Common::HousingObject obj {}; + memset( &obj, 0x0, sizeof( Common::HousingObject ) ); + m_housingObjects.fill( obj ); auto containers = housingMgr->getEstateInventory( getLandIdent() ); @@ -151,24 +151,24 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateYardOb auto obj = housingMgr->getYardObjectForItem( housingItem ); - m_yardObjects[ offset ] = obj; + m_housingObjects[ offset ] = obj; } containerIdx++; } } -void Sapphire::World::Territory::Housing::HousingInteriorTerritory::spawnYardObject( uint8_t containerIdx, - uint16_t slot, - uint16_t containerType, - Inventory::HousingItemPtr item ) +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::spawnHousingObject( uint8_t containerIdx, + uint16_t slot, + uint16_t containerType, + Inventory::HousingItemPtr item ) { auto housingMgr = g_fw.get< Manager::HousingMgr >(); auto offset = ( containerIdx * 50 ) + slot; auto obj = housingMgr->getYardObjectForItem( item ); - m_yardObjects[ offset ] = obj; + m_housingObjects[ offset ] = obj; for( const auto& player : m_playerMap ) { @@ -185,11 +185,11 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::spawnYardObj } } -void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateObjectPosition( uint16_t slot, - Sapphire::Common::FFXIVARR_POSITION3_U16 pos, - uint16_t rot ) +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateHousingObjectPosition( uint16_t slot, + Sapphire::Common::FFXIVARR_POSITION3_U16 pos, + uint16_t rot ) { - auto& obj = m_yardObjects[ slot ]; + auto& obj = m_housingObjects[ slot ]; obj.pos = pos; obj.itemRotation = rot; diff --git a/src/world/Territory/Housing/HousingInteriorTerritory.h b/src/world/Territory/Housing/HousingInteriorTerritory.h index d928cd27..f77fc49a 100644 --- a/src/world/Territory/Housing/HousingInteriorTerritory.h +++ b/src/world/Territory/Housing/HousingInteriorTerritory.h @@ -24,14 +24,15 @@ namespace Sapphire::World::Territory::Housing const Common::LandIdent getLandIdent() const; - void updateYardObjects(); - void spawnYardObject( uint8_t containerIdx, uint16_t slot, uint16_t containerType, Inventory::HousingItemPtr item ); - void updateObjectPosition( uint16_t slot, Common::FFXIVARR_POSITION3_U16 pos, uint16_t rot ); + void updateHousingObjects(); + void spawnHousingObject( uint8_t containerIdx, uint16_t slot, uint16_t containerType, + Inventory::HousingItemPtr item ); + void updateHousingObjectPosition( uint16_t slot, Common::FFXIVARR_POSITION3_U16 pos, uint16_t rot ); private: Common::LandIdent m_landIdent; uint32_t m_lastActivityTime; - std::array< Sapphire::Common::YardObject, 400 > m_yardObjects; + std::array< Sapphire::Common::HousingObject, 400 > m_housingObjects; }; } \ No newline at end of file diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index d7ff7e08..07bac271 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -104,8 +104,8 @@ bool Sapphire::HousingZone::init() } // zero out the yard obj arrays so we don't leak memory like SE does :^) - Common::YardObject obj {}; - memset( &obj, 0x0, sizeof( Common::YardObject ) ); + Common::HousingObject obj {}; + memset( &obj, 0x0, sizeof( Common::HousingObject ) ); for( auto& arr : m_yardObjects ) { @@ -175,7 +175,7 @@ void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player ) housingObjectInit->data().packetNum = yardPacketNum; housingObjectInit->data().packetTotal = yardPacketTotal; - auto yardObjectSize = sizeof( Common::YardObject ); + auto yardObjectSize = sizeof( Common::HousingObject ); auto& objects = m_yardObjects[ isInSubdivision ? 1 : 0 ]; @@ -345,17 +345,17 @@ void Sapphire::HousingZone::updateYardObjects( Sapphire::Common::LandIdent ident } } -void Sapphire::HousingZone::spawnYardObject( uint8_t landId, uint16_t slotId, Inventory::HousingItemPtr item ) +void Sapphire::HousingZone::spawnYardObject( uint8_t landId, uint16_t slotId, Inventory::HousingItem& item ) { auto bounds = m_yardObjectArrayBounds[ landId ]; auto offset = bounds.first + slotId; - Common::YardObject obj {}; + Common::HousingObject obj {}; - obj.itemId = item->getAdditionalData(); - obj.itemRotation = item->getRot(); + obj.itemId = item.getAdditionalData(); + obj.itemRotation = item.getRot(); - obj.pos = item->getPos(); + obj.pos = item.getPos(); // link obj auto yardMapIndex = landId <= 29 ? 0 : 1; @@ -370,6 +370,31 @@ void Sapphire::HousingZone::spawnYardObject( uint8_t landId, uint16_t slotId, In packet->data().objectArray = static_cast< uint8_t >( slotId ); packet->data().object = obj; + player.second->queuePacket( packet ); + } +} + +void Sapphire::HousingZone::updateYardObjectPos( uint16_t slot, uint16_t landId, Inventory::HousingItem& item ) +{ + auto bounds = m_yardObjectArrayBounds[ landId ]; + auto offset = bounds.first + slot; + auto yardMapIndex = landId <= 29 ? 0 : 1; + + auto& obj = m_yardObjects[ yardMapIndex ][ offset ]; + + obj.itemRotation = item.getRot(); + obj.pos = item.getPos(); + + for( const auto& player : m_playerMap ) + { + auto packet = makeZonePacket< Server::FFXIVIpcYardObjectMove >( player.second->getId() ); + + packet->data().itemRotation = item.getRot(); + packet->data().pos = item.getPos(); + + packet->data().landId = landId; + packet->data().objectArray = slot; + player.second->queuePacket( packet ); } } \ No newline at end of file diff --git a/src/world/Territory/HousingZone.h b/src/world/Territory/HousingZone.h index 8b14f711..b8295afe 100644 --- a/src/world/Territory/HousingZone.h +++ b/src/world/Territory/HousingZone.h @@ -55,11 +55,12 @@ namespace Sapphire Entity::EventObjectPtr registerEstateEntranceEObj( uint8_t landId ); void updateYardObjects( Common::LandIdent ident ); - void spawnYardObject( uint8_t landId, uint16_t slotId, Sapphire::Inventory::HousingItemPtr item ); + void spawnYardObject( uint8_t landId, uint16_t slotId, Sapphire::Inventory::HousingItem& item ); + void updateYardObjectPos( uint16_t slot, uint16_t landId, Inventory::HousingItem& item ); private: using LandPtrMap = std::unordered_map< uint8_t, Sapphire::LandPtr >; - using YardObjectArray = std::array< Common::YardObject, 800 >; + using YardObjectArray = std::array< Common::HousingObject, 800 >; using YardObjectSubdivisionArray = std::array< YardObjectArray, 2 >; /*!