diff --git a/src/common/Common.h b/src/common/Common.h index c06868c6..1596cfe4 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -759,7 +759,16 @@ namespace Core::Common uint8_t color[ 8 ]; // 36 }; - struct HousePermissionSet + enum LandPermissionSlot + { + FreeCompany, + Private, + Apartment, + SharedHouse1, + SharedHouse2 + }; + + struct LandPermissionSet { int16_t landSetId; //00 int16_t wardNum; //02 diff --git a/src/common/Exd/ExdDataGenerated.cpp b/src/common/Exd/ExdDataGenerated.cpp index 5359bbc7..aafcb458 100644 --- a/src/common/Exd/ExdDataGenerated.cpp +++ b/src/common/Exd/ExdDataGenerated.cpp @@ -2745,6 +2745,8 @@ Core::Data::HousingLandSet::HousingLandSet( uint32_t row_id, Core::Data::ExdData auto row = exdData->m_HousingLandSetDat.get_row( row_id ); for ( int i = 0; i < 60; i++ ) sizes.push_back( exdData->getField< uint8_t >( row, i ) ); + for ( int i = 60; i < 60 + 60; i++ ) + minPrices.push_back( exdData->getField< uint32_t >( row, i ) ); for ( int i = 300; i < 300 + 60; i++ ) prices.push_back( exdData->getField< uint32_t >( row, i ) ); diff --git a/src/common/Exd/ExdDataGenerated.h b/src/common/Exd/ExdDataGenerated.h index d77e606e..b4da203b 100644 --- a/src/common/Exd/ExdDataGenerated.h +++ b/src/common/Exd/ExdDataGenerated.h @@ -2853,6 +2853,7 @@ struct HousingPlacement struct HousingLandSet { std::vector< uint8_t > sizes; + std::vector< uint32_t > minPrices; std::vector< uint32_t > prices; HousingLandSet( uint32_t row_id, Core::Data::ExdDataGenerated* exdData ); diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 0d510483..ee2833e2 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -212,6 +212,9 @@ enum ActorControlType : BeginReplayAck = 0x3A1, EndReplayAck = 0x3A2, + // Housing + ShowHousingItemUI = 0x3F7, + // PvP Duel SetPvPState = 0x5E0, // param3 must be 6 to engage a duel (hardcoded in the client) EndDuelSession = 0x5E1, // because someone went oob? @@ -287,6 +290,7 @@ enum ClientTriggerType AchievementList = 0x3E9, RequestHousingSign = 0x451, + RequestHousingItemUI = 0x463, RequestSharedEstateSettings = 0x46F, CompanionAction = 0x6A4, diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 15da9755..b17910cf 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1578,21 +1578,21 @@ struct FFXIVIpcPerformNote : FFXIVIpcBasePacket< PerformNote > //IPCs struct FFXIVIpcLandPermission : FFXIVIpcBasePacket { - Common::HousePermissionSet freeCompanyHouse; // 00 + Common::LandPermissionSet freeCompanyHouse; // 00 uint64_t unkown1; - Common::HousePermissionSet privateHouse; // 24 + Common::LandPermissionSet privateHouse; // 24 uint64_t unkown2; - Common::HousePermissionSet apartment; // 48 + Common::LandPermissionSet apartment; // 48 uint64_t unkown3; - Common::HousePermissionSet sharedHouse[2]; //72 + Common::LandPermissionSet sharedHouse[2]; //72 uint64_t unkown4; - Common::HousePermissionSet unkownHouse; + Common::LandPermissionSet unkownHouse; uint64_t unkown5; }; struct FFXIVIpcLandUpdate : FFXIVIpcBasePacket< LandUpdate > { - uint16_t landSetId; + uint16_t landId; uint16_t unknow0; uint16_t unknow1; uint16_t unknow2; diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index 658727bc..e989149f 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -89,6 +89,12 @@ Core::Entity::Player::Player() : memset( m_classArray, 0, sizeof( m_classArray ) ); memset( m_expArray, 0, sizeof( m_expArray ) ); + for ( uint8_t i = 0; i < 5; i++ ) + { + memset( &m_housePermission[i], 0xFF, 8 ); + memset( &m_housePermission[i].permissionMask, 0, 8 ); + } + m_objSpawnIndexAllocator.init( MAX_DISPLAYED_EOBJS ); m_actorSpawnIndexAllocator.init( MAX_DISPLAYED_ACTORS, true ); } @@ -1568,48 +1574,7 @@ void Core::Entity::Player::sendZonePackets() sendItemLevel(); } - struct HousePermissionSet - { - int16_t landSetId; //00 - int16_t wardNum; //02 - int16_t zoneId; //04 - int16_t worldId; //06 - uint32_t permissionMask; //08 - uint32_t unkown1; //12 - }; - - auto landPermissions = makeZonePacket< FFXIVIpcLandPermission >( getId() ); - landPermissions->data().freeCompanyHouse.landSetId = -1; - landPermissions->data().freeCompanyHouse.wardNum = -1; - landPermissions->data().freeCompanyHouse.zoneId = -1; - landPermissions->data().freeCompanyHouse.worldId = -1; - landPermissions->data().unkown1 = 0; - landPermissions->data().privateHouse.landSetId = -1; - landPermissions->data().privateHouse.wardNum = -1; - landPermissions->data().privateHouse.zoneId = -1; - landPermissions->data().privateHouse.worldId = -1; - landPermissions->data().unkown2 = 0; - landPermissions->data().apartment.landSetId = -1; - landPermissions->data().apartment.wardNum = -1; - landPermissions->data().apartment.zoneId = -1; - landPermissions->data().apartment.worldId = -1; - landPermissions->data().unkown3 = 0; - landPermissions->data().sharedHouse[0].landSetId = -1; - landPermissions->data().sharedHouse[0].wardNum = -1; - landPermissions->data().sharedHouse[0].zoneId = -1; - landPermissions->data().sharedHouse[0].worldId = -1; - landPermissions->data().sharedHouse[1].landSetId = -1; - landPermissions->data().sharedHouse[1].wardNum = -1; - landPermissions->data().sharedHouse[1].zoneId = -1; - landPermissions->data().sharedHouse[1].worldId = -1; - landPermissions->data().unkown4 = 0; - landPermissions->data().unkownHouse.landSetId = -1; - landPermissions->data().unkownHouse.wardNum = -1; - landPermissions->data().unkownHouse.zoneId = -1; - landPermissions->data().unkownHouse.worldId = -1; - landPermissions->data().unkown5 = 2; - queuePacket( landPermissions ); - + sendLandPermissions(); auto initZonePacket = makeZonePacket< FFXIVIpcInitZone >( getId() ); initZonePacket->data().zoneId = getCurrentZone()->getTerritoryTypeId(); @@ -1786,3 +1751,33 @@ bool Core::Entity::Player::isOnEnterEventDone() const { return m_onEnterEventDone; } + +void Core::Entity::Player::setLandPermissions( uint8_t permissionSet, uint32_t permissionMask, int16_t landSetId, int16_t wardNum, int16_t zoneId ) +{ + m_housePermission[permissionSet].landSetId = landSetId; + m_housePermission[permissionSet].permissionMask = permissionMask; + m_housePermission[permissionSet].wardNum = wardNum; + m_housePermission[permissionSet].worldId = 67; + m_housePermission[permissionSet].unkown1 = 0; +} + +void Core::Entity::Player::sendLandPermissions() +{ + auto landPermissions = makeZonePacket< FFXIVIpcLandPermission >( getId() ); + + landPermissions->data().freeCompanyHouse = m_housePermission[Common::LandPermissionSlot::FreeCompany]; + landPermissions->data().privateHouse = m_housePermission[Common::LandPermissionSlot::Private]; + landPermissions->data().apartment = m_housePermission[Common::LandPermissionSlot::Apartment]; + landPermissions->data().sharedHouse[0] = m_housePermission[Common::LandPermissionSlot::SharedHouse1]; + landPermissions->data().sharedHouse[1] = m_housePermission[Common::LandPermissionSlot::SharedHouse2]; + memset( &landPermissions->data().unkownHouse, 0xFF, 8 ); + memset( &landPermissions->data().unkownHouse.permissionMask, 0, 8 ); + landPermissions->data().unkownHouse.permissionMask = 2; + landPermissions->data().unkown1 = 0; + landPermissions->data().unkown2 = 0; + landPermissions->data().unkown3 = 0; + landPermissions->data().unkown4 = 0; + landPermissions->data().unkown5 = 0; + + queuePacket( landPermissions ); +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index 1bf9fe72..f0aefd21 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -761,6 +761,12 @@ namespace Core::Entity void setDirectorInitialized( bool isInitialized ); + // Housing Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + void setLandPermissions( uint8_t permissionSet, uint32_t permissionMask, int16_t landSetId, int16_t wardNum, int16_t zoneId ); + + void sendLandPermissions(); + // Player Battle Handling ////////////////////////////////////////////////////////////////////////////////////////////////////// void initHateSlotQueue(); @@ -1013,6 +1019,9 @@ namespace Core::Entity uint8_t m_searchSelectRegion; // regions selected to show up in profile uint8_t m_searchSelectClass; // class selected to show up in profile + // housing info + Common::LandPermissionSet m_housePermission[5]; + // gc info uint8_t m_gc; uint8_t m_gcRank[3]; diff --git a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp index 276acd3a..24108b0e 100644 --- a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp @@ -27,6 +27,7 @@ #include "Actor/BNpc.h" #include "Zone/Zone.h" +#include "Zone/HousingZone.h" #include "Zone/InstanceContent.h" #include "Zone/TerritoryMgr.h" #include "Event/EventDefs.h" @@ -57,6 +58,7 @@ Core::DebugCommandHandler::DebugCommandHandler() registerCommand( "help", &DebugCommandHandler::help, "Shows registered commands.", 0 ); registerCommand( "script", &DebugCommandHandler::script, "Server script utilities.", 1 ); registerCommand( "instance", &DebugCommandHandler::instance, "Instance utilities", 1 ); + registerCommand( "housing", &DebugCommandHandler::housing, "Housing utilities", 1 ); } // clear all loaded commands @@ -981,3 +983,50 @@ Core::DebugCommandHandler::instance( char* data, Entity::Player& player, std::sh player.sendDebug( "Unknown sub command." ); } } + +void Core::DebugCommandHandler::housing( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ) +{ + auto pTeriMgr = g_fw.get< TerritoryMgr >(); + std::string cmd( data ), params, subCommand; + auto cmdPos = cmd.find_first_of( ' ' ); + + if( cmdPos != std::string::npos ) + { + params = cmd.substr( cmdPos + 1 ); + + auto p = params.find_first_of( ' ' ); + + if( p != std::string::npos ) + { + subCommand = params.substr( 0, p ); + params = params.substr( subCommand.length() + 1 ); + } + else + subCommand = params; + } + + if( subCommand == "permission" || subCommand == "perm" ) + { + uint8_t permissionSet; + sscanf( params.c_str(), "%hhu", &permissionSet ); + + if ( permissionSet < 5 ) + { + auto pZone = player.getCurrentZone(); + if( pTeriMgr->isHousingTerritory( pZone->getTerritoryTypeId() ) ) + { + auto pHousing = std::dynamic_pointer_cast< HousingZone >( pZone ); + if( pHousing ) + player.setLandPermissions( permissionSet, 8, pHousing->getLandSetId(), pHousing->getWardNum(), pHousing->getTerritoryTypeId() ); + else + player.sendDebug( "You aren't in a housing Zone." ); + } + } + else + player.sendDebug( "PermissionSet out of range." ); + } + else + { + player.sendDebug( "Unknown sub command." ); + } +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.h b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.h index 89373038..3a9ebc80 100644 --- a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.h +++ b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.h @@ -53,6 +53,8 @@ namespace Core void instance( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ); + void housing( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command) ; + void script( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ); }; diff --git a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp index dfffc85c..cb30f491 100644 --- a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp @@ -326,11 +326,23 @@ void Core::Network::GameConnection::clientTriggerHandler( const Packets::FFXIVAR auto land = hZone->getLand( plot ); plotPricePacket->data().price = land->getCurrentPrice(); + plotPricePacket->data().timeLeft = land->getDevaluationTime(); player.queuePacket( plotPricePacket ); break; } + case ClientTriggerType::RequestHousingItemUI: + { + uint32_t plot = param2; + auto pShowHousingItemUIPacket = makeActorControl142( player.getId(), ShowHousingItemUI, 0, plot ); + + player.queuePacket( pShowHousingItemUIPacket ); + + //TODO: show item housing container + + break; + } default: { diff --git a/src/servers/sapphire_zone/Zone/HousingZone.cpp b/src/servers/sapphire_zone/Zone/HousingZone.cpp index 094425eb..22ee225e 100644 --- a/src/servers/sapphire_zone/Zone/HousingZone.cpp +++ b/src/servers/sapphire_zone/Zone/HousingZone.cpp @@ -131,6 +131,20 @@ void Core::HousingZone::sendLandSet( Entity::Player& player ) player.queuePacket( landsetInitializePacket ); } +void Core::HousingZone::sendLandUpdate( uint8_t landId ) +{ + for( const auto& playerIt : m_playerMap ) + { + auto pPlayer = playerIt.second; + + auto landUpdatePacket = makeZonePacket< FFXIVIpcLandUpdate >( pPlayer->getId() ); + landUpdatePacket->data().landId = landId; + landUpdatePacket->data().land = getLand( landId )->getLand(); + + pPlayer->queuePacket( landUpdatePacket ); + } +} + bool Core::HousingZone::isPlayerSubInstance( Entity::Player& player ) { return player.getPos().x < -15000.0f; //ToDo: get correct pos diff --git a/src/servers/sapphire_zone/Zone/HousingZone.h b/src/servers/sapphire_zone/Zone/HousingZone.h index 4d265df2..c2c6619f 100644 --- a/src/servers/sapphire_zone/Zone/HousingZone.h +++ b/src/servers/sapphire_zone/Zone/HousingZone.h @@ -23,6 +23,7 @@ namespace Core void onUpdate( uint32_t currTime ) override; void sendLandSet( Entity::Player& player ); + void sendLandUpdate( uint8_t landId ); bool isPlayerSubInstance( Entity::Player& player ); /* returns current ward number for this zone */ diff --git a/src/servers/sapphire_zone/Zone/Land.cpp b/src/servers/sapphire_zone/Zone/Land.cpp index d9575b31..44828849 100644 --- a/src/servers/sapphire_zone/Zone/Land.cpp +++ b/src/servers/sapphire_zone/Zone/Land.cpp @@ -31,6 +31,7 @@ Core::Land::Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t lan m_wardNum( wardNum ), m_landId( landId ), m_currentPrice( 0 ), + m_minPrice( 0 ), m_nextDrop( 0 ), m_landSetId( landSetId ), m_landInfo( info ) @@ -46,7 +47,6 @@ Core::Land::~Land() void Core::Land::load() { - m_land.houseState = HouseState::forSale; auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); @@ -54,14 +54,13 @@ void Core::Land::load() "AND landid = " + std::to_string( m_landId ) ); if( !res->next() ) { - - pDb->directExecute( "INSERT INTO land ( landsetid, landid, size, status, landprice ) " "VALUES ( " + std::to_string( m_landSetId ) + "," + std::to_string( m_landId ) + "," + std::to_string( m_landInfo->sizes[ m_landId ] ) + "," + " 1, " + std::to_string( m_landInfo->prices[ m_landId ] ) + " );" ); m_currentPrice = m_landInfo->prices[ m_landId ]; + m_minPrice = m_landInfo->minPrices[ m_landId ]; m_land.houseSize = m_landInfo->sizes[ m_landId ]; } else @@ -237,6 +236,11 @@ uint32_t Core::Land::getMaxItems() return m_maxItems; } +uint32_t Core::Land::getDevaluationTime() +{ + return m_nextDrop - Util::getTimeSeconds(); +} + void Core::Land::init() { @@ -269,22 +273,13 @@ void Core::Land::UpdateDatabase() void Core::Land::Update( uint32_t currTime ) { - if( m_currentPrice == 0 && getState() == HouseState::forSale ) + if( getState() == HouseState::forSale ) { - m_currentPrice = m_initPrice; - m_nextDrop = 0; + if( m_nextDrop < currTime && m_minPrice < m_currentPrice ) + { + m_nextDrop = currTime + 21600; + m_currentPrice = ( m_currentPrice / 100 ) * 99.58; + } UpdateDatabase(); } - if( m_nextDrop < currTime && getState() == HouseState::forSale ) - { - m_currentPrice = ( m_currentPrice / 100 ) * 90; - m_nextDrop = currTime + 86400; - UpdateDatabase(); - } - onUpdate(); -} - -void Core::Land::onUpdate() -{ - } \ No newline at end of file diff --git a/src/servers/sapphire_zone/Zone/Land.h b/src/servers/sapphire_zone/Zone/Land.h index c3f958f8..cc1b6ac5 100644 --- a/src/servers/sapphire_zone/Zone/Land.h +++ b/src/servers/sapphire_zone/Zone/Land.h @@ -52,12 +52,12 @@ namespace Core void setPreset( uint32_t itemId ); void UpdateDatabase(); void Update( uint32_t currTime ); - void onUpdate(); const Common::LandStruct& getLand(); uint32_t getMaxItems(); uint32_t getCurrentPrice() const; + uint32_t getDevaluationTime(); private: uint16_t convertItemIdToHousingItemId( uint16_t itemId ); @@ -80,6 +80,7 @@ namespace Core uint32_t m_initPrice; uint32_t m_nextDrop; uint32_t m_currentPrice; + uint32_t m_minPrice; }; }