diff --git a/src/common/Common.h b/src/common/Common.h index 543cf72d..fe8eb6bc 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -805,8 +805,8 @@ namespace Core::Common none, forSale, sold, + privateHouse, fcHouse, - privateHouse }; enum HouseIconAdd : uint8_t diff --git a/src/common/Exd/ExdDataGenerated.h b/src/common/Exd/ExdDataGenerated.h index 6eda3351..af2974ef 100644 --- a/src/common/Exd/ExdDataGenerated.h +++ b/src/common/Exd/ExdDataGenerated.h @@ -6127,7 +6127,7 @@ struct ZoneSharedGroup } catch( std::runtime_error error ) { - std::cout << error.what(); + // std::cout << error.what(); return nullptr; } return nullptr; diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index e028e136..5d126c49 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -290,6 +290,8 @@ enum ActorControlType : uint16_t AchievementList = 0x3E9, RequestHousingBuildPreset = 0x44C, + RequestEstateHallRemoval = 0x44F, + RequestBuildPreset = 0x450, // no idea what this is, it gets sent with BuildPresetHandler and has the plot id in param1 RequestLandSignFree = 0x451, RequestLandSignOwned = 0x452, RequestWardLandInfo = 0x453, diff --git a/src/servers/Scripts/common/GilShop.cpp b/src/servers/Scripts/common/GilShop.cpp index e6c988ea..6554ca72 100644 --- a/src/servers/Scripts/common/GilShop.cpp +++ b/src/servers/Scripts/common/GilShop.cpp @@ -1,6 +1,9 @@ #include #include +#include +#include + using namespace Core; class GilShop : @@ -16,11 +19,11 @@ public: void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override { - player.playScene( eventId, 0, SCENE_FLAGS, 0, 2, shopCallback ); + player.playScene( eventId, 0, SCENE_FLAGS, 0, 2, std::bind( &GilShop::shopCallback, this, std::placeholders::_1, std::placeholders::_2 ) ); } private: - static void shopInteractionCallback( Entity::Player& player, const Event::SceneResult& result ) + void shopInteractionCallback( Entity::Player& player, const Event::SceneResult& result ) { // item purchase if( result.param1 == 768 ) @@ -28,17 +31,20 @@ private: // buy if( result.param2 == 1 ) { + auto shopMgr = getFramework()->get< Sapphire::World::Manager::ShopMgr >(); + shopMgr->purchaseGilShopItem( player, result.eventId, result.param3, result.param4 ); } - // sell + // sell else if( result.param2 == 2 ) { - + // so apparently shops will always show a sell window + // BUT won't always let you sell stuff (eg, housing permit menu) + // there doesn't seem to be anything in gilshop exd for that, so maybe it's some shitty server hack? } - player.sendDebug( "got tradeQuantity: " + std::to_string( result.param4 ) ); - player.playGilShop( result.eventId, SCENE_FLAGS, shopInteractionCallback ); + player.playGilShop( result.eventId, SCENE_FLAGS, std::bind( &GilShop::shopInteractionCallback, this, std::placeholders::_1, std::placeholders::_2 ) ); return; } @@ -46,8 +52,8 @@ private: player.playScene( result.eventId, 255, SCENE_FLAGS ); } - static void shopCallback( Entity::Player& player, const Event::SceneResult& result ) + void shopCallback( Entity::Player& player, const Event::SceneResult& result ) { - player.playGilShop( result.eventId, SCENE_FLAGS, shopInteractionCallback ); + player.playGilShop( result.eventId, SCENE_FLAGS, std::bind( &GilShop::shopInteractionCallback, this, std::placeholders::_1, std::placeholders::_2 ) ); } }; \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index 7aba3e56..62e6143a 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -1580,7 +1580,7 @@ void Core::Entity::Player::sendZonePackets() auto pHousingMgr = g_fw.get< HousingMgr >(); if( Core::LandPtr pLand = pHousingMgr->getLandByOwnerId( getId() ) ) { - setLandPermissions( LandPermissionSlot::Private, 0x00, pLand->getLandId(), pLand->getWardNum(), pLand->getZoneId() ); + setLandPermissions( LandPermissionSlot::Private, 0x00, pLand->getLandId(), pLand->getWardNum(), pLand->getTerritoryTypeId() ); } sendLandPermissions(); diff --git a/src/servers/sapphire_zone/Manager/ShopMgr.cpp b/src/servers/sapphire_zone/Manager/ShopMgr.cpp new file mode 100644 index 00000000..78173a1f --- /dev/null +++ b/src/servers/sapphire_zone/Manager/ShopMgr.cpp @@ -0,0 +1,34 @@ +#include "ShopMgr.h" + +#include +#include +#include +#include + +extern Core::Framework g_fw; +using namespace Core; + +bool Sapphire::World::Manager::ShopMgr::purchaseGilShopItem( Entity::Player& player, uint32_t shopId, uint16_t itemId, uint32_t quantity ) +{ + auto exdData = g_fw.get< Data::ExdDataGenerated >(); + if( !exdData ) + return false; + + auto gilShopItem = exdData->get< Data::GilShopItem >( shopId, itemId ); + if( !gilShopItem ) + return false; + + auto item = exdData->get< Data::Item >( gilShopItem->item ); + if( !item ) + return false; + + if( player.getCurrency( Common::CurrencyType::Gil ) < item->priceMid ) + return false; + + if( !player.addItem( gilShopItem->item, quantity ) ) + return false; + + player.removeCurrency( Common::CurrencyType::Gil, item->priceMid ); + + return true; +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Manager/ShopMgr.h b/src/servers/sapphire_zone/Manager/ShopMgr.h new file mode 100644 index 00000000..8ec5de4d --- /dev/null +++ b/src/servers/sapphire_zone/Manager/ShopMgr.h @@ -0,0 +1,10 @@ +#include "ForwardsZone.h" + +namespace Sapphire::World::Manager +{ + class ShopMgr + { + public: + bool purchaseGilShopItem( Core::Entity::Player& player, uint32_t shopId, uint16_t itemId, uint32_t quantity ); + }; +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp index d6d77bb2..becbcdc5 100644 --- a/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/EventHandlers.cpp @@ -273,7 +273,9 @@ void Core::Network::GameConnection::eventHandlerShop( const Packets::FFXIVARR_PA " (0x" + Util::intToHexString( static_cast< uint64_t >( eventId & 0xFFFFFFF ), 8 ) + ")" ); player.sendDebug( "Calling: " + objName + "." + eventName ); - player.eventStart( 0, eventId, Event::EventHandler::UI, 0, packet.data().param ); + player.eventStart( player.getId(), eventId, Event::EventHandler::UI, 0, packet.data().param ); + + pScriptMgr->onTalk( player, player.getId(), eventId ); } diff --git a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp index c7604ba8..821b9b3d 100644 --- a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp @@ -673,40 +673,6 @@ void Core::Network::GameConnection::buildPresetHandler( const Core::Network::Pac { const auto packet = ZoneChannelPacket< Client::FFXIVIpcBuildPresetHandler >( inPacket ); - auto zone = player.getCurrentZone(); - auto plotNum = packet.data().plotNum; - auto preset = packet.data().itemId; - std::string landString = std::string( packet.data().stateString ); - auto hZone = std::dynamic_pointer_cast< HousingZone >( zone ); - - if( !hZone ) - return; - - auto pLand = hZone->getLand( plotNum ); - /* - if (!pLand) - player.sendDebug( "Something went wrong..." ); - - if( stateString.find( "Private" ) ) - { - pLand->setPreset( preset ); - pLand->setState( HouseState::privateHouse ); - pLand->UpdateLandDb(); - hZone->sendLandUpdate( plotNum ); - } - else if( stateString.find("Free") ) - { - pLand->setPreset( preset ); - pLand->setState( HouseState::fcHouse ); - pLand->UpdateLandDb(); - hZone->sendLandUpdate( plotNum ); - } - else - { - player.sendDebug( "You tried to build a preset on not supported land." ); - } - - auto pSuccessBuildingPacket = makeActorControl142( player.getId(), BuildPresetResponse, plotNum ); - - player.queuePacket( pSuccessBuildingPacket );*/ + auto pHousingMgr = g_fw.get< HousingMgr >(); + pHousingMgr->buildPresetEstate( player, packet.data().plotNum, packet.data().itemId ); } diff --git a/src/servers/sapphire_zone/Zone/House.cpp b/src/servers/sapphire_zone/Zone/House.cpp index 7b9a74b3..55ca5b1d 100644 --- a/src/servers/sapphire_zone/Zone/House.cpp +++ b/src/servers/sapphire_zone/Zone/House.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "House.h" @@ -20,6 +21,18 @@ Core::House::House( uint32_t houseId, uint32_t landSetId, uint8_t landId, uint8_ { memset( &m_houseParts, 0x00, sizeof( m_houseParts ) ); memset( &m_commentMsg, 0x00, sizeof( m_commentMsg ) ); + + auto pDB = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); + auto res = pDB->query("SELECT * FROM house WHERE HouseId = " + std::to_string( houseId ) ); + + if( !res->next() ) + { + pDB->directExecute("INSERT INTO house ( LandSetId, HouseId ) VALUES ( " + std::to_string( m_landSetId ) + ", " + std::to_string( m_houseId ) + " )" ); + } + else + { + // todo + } } Core::House::~House() @@ -70,4 +83,4 @@ void Core::House::setHousePartColor( Common::HousePartSlot slot, uint32_t id ) uint32_t Core::House::getHousePart( Common::HousePartSlot slot ) const { return m_houseParts[ slot ]; -} +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Zone/HousingMgr.cpp b/src/servers/sapphire_zone/Zone/HousingMgr.cpp index c9d50ca0..28c18adf 100644 --- a/src/servers/sapphire_zone/Zone/HousingMgr.cpp +++ b/src/servers/sapphire_zone/Zone/HousingMgr.cpp @@ -98,7 +98,7 @@ void Core::HousingMgr::sendLandSignOwned( Entity::Player& player, uint8_t wardId memcpy( &landInfoSignPacket->data().ownerName, playerName.c_str(), playerName.size() ); landInfoSignPacket->data().wardNum = land->getWardNum(); landInfoSignPacket->data().worldId = 67; - landInfoSignPacket->data().zoneId = land->getZoneId(); + landInfoSignPacket->data().zoneId = land->getTerritoryTypeId(); player.queuePacket( landInfoSignPacket ); } @@ -273,3 +273,32 @@ void Core::HousingMgr::sendWardLandInfo( Entity::Player& player, uint8_t wardId, player.queuePacket( wardInfoPacket ); } +void Core::HousingMgr::buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetItem ) +{ + auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() ); + + if( !hZone ) + return; + + auto pLand = hZone->getLand( plotNum ); + if( !pLand ) + return; + + // todo: when doing FC houses, look up the type from the original purchase and check perms from FC and set state accordingly + if( pLand->getPlayerOwner() != player.getId() ) + return; + + // todo: check if permit is in inventory and remove one + + if( !pLand->setPreset( presetItem ) ) + return; + + pLand->setState( HouseState::privateHouse ); + pLand->setLandType( LandType::Private ); + pLand->updateLandDb(); + hZone->sendLandUpdate( plotNum ); + + auto pSuccessBuildingPacket = makeActorControl142( player.getId(), ActorControl::BuildPresetResponse, plotNum ); + + player.queuePacket( pSuccessBuildingPacket ); +} diff --git a/src/servers/sapphire_zone/Zone/HousingMgr.h b/src/servers/sapphire_zone/Zone/HousingMgr.h index bc5d94d8..cacaa9c1 100644 --- a/src/servers/sapphire_zone/Zone/HousingMgr.h +++ b/src/servers/sapphire_zone/Zone/HousingMgr.h @@ -34,6 +34,8 @@ namespace Core bool relinquishLand( Entity::Player& player, uint8_t plot ); + void buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetItem ); + private: }; diff --git a/src/servers/sapphire_zone/Zone/HousingZone.cpp b/src/servers/sapphire_zone/Zone/HousingZone.cpp index 9d52a1f4..8ca52012 100644 --- a/src/servers/sapphire_zone/Zone/HousingZone.cpp +++ b/src/servers/sapphire_zone/Zone/HousingZone.cpp @@ -10,6 +10,7 @@ #include "Actor/Player.h" #include "Actor/Actor.h" #include "Land.h" +#include "House.h" #include "Forwards.h" #include "HousingZone.h" @@ -149,12 +150,24 @@ void Core::HousingZone::sendLandUpdate( uint8_t landId ) landUpdatePacket->data().landId = landId; landUpdatePacket->data().land.plotSize = pLand->getSize(); landUpdatePacket->data().land.houseState = pLand->getState(); - landUpdatePacket->data().land.type = static_cast< uint8_t >( pLand->getLandType() ); + landUpdatePacket->data().land.type = 0; landUpdatePacket->data().land.iconAddIcon = pLand->getSharing(); landUpdatePacket->data().land.fcId = pLand->getFcId(); landUpdatePacket->data().land.fcIcon = pLand->getFcIcon(); landUpdatePacket->data().land.fcIconColor = pLand->getFcColor(); + if( auto house = pLand->getHouse() ) + { + // todo: this is retarded, need a getter to the internal array + for( int i = 0; i < 8; i++ ) + { + auto slot = static_cast< Common::HousePartSlot >( i ); + auto part = pLand->getHouse()->getHousePart( slot ); + + landUpdatePacket->data().land.housePart[ slot ] = part; + } + } + pPlayer->queuePacket( landUpdatePacket ); } } diff --git a/src/servers/sapphire_zone/Zone/Land.cpp b/src/servers/sapphire_zone/Zone/Land.cpp index c39701af..3fbcd3ae 100644 --- a/src/servers/sapphire_zone/Zone/Land.cpp +++ b/src/servers/sapphire_zone/Zone/Land.cpp @@ -20,14 +20,15 @@ #include "Forwards.h" #include "Land.h" #include "Framework.h" +#include "House.h" extern Core::Framework g_fw; using namespace Core::Common; -Core::Land::Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, +Core::Land::Land( uint16_t territoryTypeId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, Core::Data::HousingLandSetPtr info ) : - m_zoneId( zoneId ), + m_territoryTypeId( territoryTypeId ), m_wardNum( wardNum ), m_landId( landId ), m_currentPrice( 0 ), @@ -36,7 +37,11 @@ Core::Land::Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t lan m_ownerPlayerId( 0 ), m_landSetId( landSetId ), m_landInfo( info ), - m_type( Common::LandType::Private ) + m_type( Common::LandType::Private ), + m_fcIcon( 0 ), + m_fcIconColor( 0 ), + m_fcId( 0 ), + m_iconAddIcon( 0 ) { memset( &m_tag, 0x00, 3 ); @@ -79,7 +84,7 @@ void Core::Land::load() init(); } -uint16_t Core::Land::convertItemIdToHousingItemId( uint16_t itemId ) +uint32_t Core::Land::convertItemIdToHousingItemId( uint32_t itemId ) { auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto info = pExdData->get< Core::Data::Item >( itemId ); @@ -147,9 +152,9 @@ uint8_t Core::Land::getLandId() const return m_landId; } -uint16_t Core::Land::getZoneId() const +uint16_t Core::Land::getTerritoryTypeId() const { - return m_zoneId; + return m_territoryTypeId; } Core::HousePtr Core::Land::getHouse() const @@ -242,12 +247,17 @@ void Core::Land::init() void Core::Land::updateLandDb() { + uint32_t houseId = 0; + + if( getHouse() ) + houseId = getHouse()->getHouseId(); + auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); pDb->directExecute( "UPDATE land SET status = " + std::to_string( m_state ) + ", LandPrice = " + std::to_string( getCurrentPrice() ) + ", UpdateTime = " + std::to_string( getDevaluationTime() ) - + ", OwnerId = " + std::to_string( getPlayerOwner() ) - + ", HouseId = " + std::to_string( 0 ) //TODO: add house id + + ", OwnerId = " + std::to_string( getPlayerOwner() ) + + ", HouseId = " + std::to_string( houseId ) + ", Type = " + std::to_string( static_cast< uint32_t >( m_type ) ) //TODO: add house id + " WHERE LandSetId = " + std::to_string( m_landSetId ) + " AND LandId = " + std::to_string( m_landId ) + ";" ); @@ -265,3 +275,41 @@ void Core::Land::update( uint32_t currTime ) } } } + +uint32_t Core::Land::getNextHouseId() +{ + auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); + auto pQR = pDb->query( "SELECT MAX( HouseId ) FROM house" ); + + if( !pQR->next() ) + return 0; + + return pQR->getUInt( 1 ) + 1; +} + +bool Core::Land::setPreset( uint32_t itemId ) +{ + auto housingItemId = convertItemIdToHousingItemId( itemId ); + + auto exdData = g_fw.get< Core::Data::ExdDataGenerated >(); + if( !exdData ) + return false; + + auto housingPreset = exdData->get< Core::Data::HousingPreset >( housingItemId ); + if( !housingPreset ) + return false; + + if( !getHouse() ) + { + // todo: i guess we'd create a house here? + auto newId = getNextHouseId(); + m_pHouse = make_House( newId, getLandSetId(), getLandId(), getWardNum(), getTerritoryTypeId() ); + } + + getHouse()->setHousePart( Common::HousePartSlot::ExteriorRoof, housingPreset->exteriorRoof ); + getHouse()->setHousePart( Common::HousePartSlot::ExteriorWall, housingPreset->exteriorWall ); + getHouse()->setHousePart( Common::HousePartSlot::ExteriorWindow, housingPreset->exteriorWindow ); + getHouse()->setHousePart( Common::HousePartSlot::ExteriorDoor, housingPreset->exteriorDoor ); + + return true; +} diff --git a/src/servers/sapphire_zone/Zone/Land.h b/src/servers/sapphire_zone/Zone/Land.h index 173a00a7..3bf792d2 100644 --- a/src/servers/sapphire_zone/Zone/Land.h +++ b/src/servers/sapphire_zone/Zone/Land.h @@ -33,7 +33,7 @@ namespace Core uint32_t getLandSetId() const; uint8_t getWardNum() const; uint8_t getLandId() const; - uint16_t getZoneId() const; + uint16_t getTerritoryTypeId() const; Common::LandType getLandType() const; Core::HousePtr getHouse() const; @@ -48,7 +48,7 @@ namespace Core uint32_t getPlayerOwner(); //Housing Functions void setCurrentPrice( uint32_t currentPrice ); - void setPreset( uint32_t itemId ); + bool setPreset( uint32_t itemId ); void updateLandDb(); void update( uint32_t currTime ); @@ -62,13 +62,14 @@ namespace Core uint8_t getLandTag( uint8_t slot ); private: - uint16_t convertItemIdToHousingItemId( uint16_t itemId ); + uint32_t convertItemIdToHousingItemId( uint32_t itemId ); void init(); + uint32_t getNextHouseId(); uint8_t m_wardNum; uint8_t m_landId; uint32_t m_landSetId; - uint16_t m_zoneId; + uint16_t m_territoryTypeId; uint8_t m_size; uint8_t m_state; Common::LandType m_type; diff --git a/src/servers/sapphire_zone/mainGameServer.cpp b/src/servers/sapphire_zone/mainGameServer.cpp index 489857b0..e6acc87d 100644 --- a/src/servers/sapphire_zone/mainGameServer.cpp +++ b/src/servers/sapphire_zone/mainGameServer.cpp @@ -12,6 +12,7 @@ #include "Zone/HousingMgr.h" #include "DebugCommand/DebugCommandHandler.h" #include "Manager/PlayerMgr.h" +#include "Manager/ShopMgr.h" #include @@ -32,6 +33,7 @@ bool setupFramework() auto pDebugCom = std::make_shared< DebugCommandHandler >(); auto pConfig = std::make_shared< ConfigMgr >(); auto pPlayerMgr = std::make_shared< Sapphire::World::Manager::PlayerMgr >(); + auto pShopMgr = std::make_shared< Sapphire::World::Manager::ShopMgr >(); pLogger->setLogPath( "log/SapphireZone" ); pLogger->init(); @@ -47,6 +49,7 @@ bool setupFramework() g_fw.set< DebugCommandHandler >( pDebugCom ); g_fw.set< ConfigMgr >( pConfig ); g_fw.set< Sapphire::World::Manager::PlayerMgr >( pPlayerMgr ); + g_fw.set< Sapphire::World::Manager::ShopMgr >( pShopMgr ); // actuall catch errors here... return true;