diff --git a/sql/houseiteminventory.sql b/sql/houseiteminventory.sql new file mode 100644 index 00000000..3746e672 --- /dev/null +++ b/sql/houseiteminventory.sql @@ -0,0 +1,9 @@ +CREATE TABLE `houseiteminventory` ( + `landIdent` BIGINT(20) UNSIGNED NOT NULL, + `containerId` INT(10) UNSIGNED NOT NULL, + `itemId` INT(20) NOT NULL, + INDEX `landIdent` (`landIdent`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +; diff --git a/src/common/Common.h b/src/common/Common.h index 2ed618a3..14de2012 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -221,7 +221,11 @@ namespace Sapphire::Common FreeCompanyBag2 = 20002, FreeCompanyGil = 22000, FreeCompanyCrystal = 22001, - HousingOutdoorItems = 25001 + + HousingExternalAppearance = 25000, + HousingOutdoorItemStoreroom = 25001, + HousingInternalAppearance = 25002, + HousingIndoorItemStoreroom = 25003, }; enum ContainerType : uint16_t @@ -823,9 +827,9 @@ namespace Sapphire::Common enum HouseSize : uint8_t { - small, - medium, - big + Cottage, + House, + Mansion }; enum HouseState : uint8_t @@ -861,7 +865,7 @@ namespace Sapphire::Common { IsEstateOwned = 1, IsPublicEstate = 2, - HasEstateMessage = 4, + HasEstateGreeting = 4, EstateFlagUnknown = 8, IsFreeCompanyEstate = 16, }; diff --git a/src/servers/sapphire_zone/Manager/HousingMgr.cpp b/src/servers/sapphire_zone/Manager/HousingMgr.cpp index e58922b2..60f04b8a 100644 --- a/src/servers/sapphire_zone/Manager/HousingMgr.cpp +++ b/src/servers/sapphire_zone/Manager/HousingMgr.cpp @@ -244,6 +244,9 @@ void Sapphire::World::Manager::HousingMgr::sendWardLandInfo( Entity::Player& pla wardInfoPacket->data().landIdent.wardNum = wardId; wardInfoPacket->data().landIdent.territoryTypeId = territoryTypeId; + // todo: properly get worldId + wardInfoPacket->data().landIdent.worldId = 67; + for( int i = 0; i < 60; i++ ) { auto land = hZone->getLand( i ); @@ -258,20 +261,26 @@ void Sapphire::World::Manager::HousingMgr::sendWardLandInfo( Entity::Player& pla if( land->getState() == Common::HouseState::forSale ) continue; + if( auto house = land->getHouse() ) + { + if( !house->getHouseGreeting().empty() ) + entry.infoFlags |= WardlandFlags::HasEstateGreeting; + } + switch( land->getLandType() ) { case LandType::FreeCompany: - entry.infoFlags = Common::WardlandFlags::IsEstateOwned | Common::WardlandFlags::IsFreeCompanyEstate; + entry.infoFlags |= Common::WardlandFlags::IsEstateOwned | Common::WardlandFlags::IsFreeCompanyEstate; // todo: send FC name break; case LandType::Private: - entry.infoFlags = Common::WardlandFlags::IsEstateOwned; + entry.infoFlags |= Common::WardlandFlags::IsEstateOwned; auto owner = land->getPlayerOwner(); - std::string playerName = g_fw.get< Sapphire::ServerMgr >()->getPlayerNameFromDb( owner ); + auto playerName = g_fw.get< Sapphire::ServerMgr >()->getPlayerNameFromDb( owner ); memcpy( &entry.estateOwnerName, playerName.c_str(), playerName.size() ); break; @@ -284,6 +293,32 @@ void Sapphire::World::Manager::HousingMgr::sendWardLandInfo( Entity::Player& pla player.queuePacket( wardInfoPacket ); } +void Sapphire::World::Manager::HousingMgr::sendEstateGreeting( Entity::Player& player, const Common::LandIdent ident ) +{ + auto landSetId = toLandSetId( ident.territoryTypeId, ident.wardNum ); + auto hZone = getHousingZoneByLandSetId( landSetId ); + + if( !hZone ) + return; + + auto land = hZone->getLand( ident.landId ); + if( !land ) + return; + + auto house = land->getHouse(); + if( !house ) + return; + + auto greetingPacket = makeZonePacket< FFXIVIpcHousingEstateGreeting >( player.getId() ); + + greetingPacket->data().landIdent = ident; + + auto greeting = house->getHouseGreeting(); + memcpy( &greetingPacket->data().message, greeting.c_str(), greeting.size() ); + + player.queuePacket( greetingPacket ); +} + void Sapphire::World::Manager::HousingMgr::buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetItem ) { auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentZone() ); diff --git a/src/servers/sapphire_zone/Manager/HousingMgr.h b/src/servers/sapphire_zone/Manager/HousingMgr.h index 73e67125..f0e3c170 100644 --- a/src/servers/sapphire_zone/Manager/HousingMgr.h +++ b/src/servers/sapphire_zone/Manager/HousingMgr.h @@ -43,6 +43,8 @@ namespace Sapphire::World::Manager void requestEstateEditGuestAccess( Entity::Player& player, uint16_t territoryTypeId, uint16_t worldId, uint8_t wardId, uint8_t plotId ); + void sendEstateGreeting( Entity::Player& player, const Common::LandIdent ident ); + }; } diff --git a/src/servers/sapphire_zone/Manager/ShopMgr.cpp b/src/servers/sapphire_zone/Manager/ShopMgr.cpp index bc37488c..9eb4ed09 100644 --- a/src/servers/sapphire_zone/Manager/ShopMgr.cpp +++ b/src/servers/sapphire_zone/Manager/ShopMgr.cpp @@ -22,13 +22,15 @@ bool Sapphire::World::Manager::ShopMgr::purchaseGilShopItem( Entity::Player& pla if( !item ) return false; - if( player.getCurrency( Common::CurrencyType::Gil ) < item->priceMid ) + auto price = item->priceMid * quantity; + + if( player.getCurrency( Common::CurrencyType::Gil ) < price ) return false; if( !player.addItem( gilShopItem->item, quantity ) ) return false; - player.removeCurrency( Common::CurrencyType::Gil, item->priceMid ); + player.removeCurrency( Common::CurrencyType::Gil, price ); return true; } \ No newline at end of file diff --git a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp index 277ac909..482e10c6 100644 --- a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp @@ -427,6 +427,28 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX break; } + case ClientTriggerType::RequestEstateGreeting: + { + uint16_t territoryTypeId = param11 & 0xFFFF; + uint16_t worldId = param11 >> 16; + + uint8_t ward = ( param12 >> 16 ) & 0xFF; + uint8_t plot = ( param12 & 0xFF ); + + auto housingMgr = g_fw.get< HousingMgr >(); + if( !housingMgr ) + break; + + Common::LandIdent ident; + ident.territoryTypeId = territoryTypeId; + ident.worldId = worldId; + ident.wardNum = ward; + ident.landId = plot; + + housingMgr->sendEstateGreeting( player, ident ); + + break; + } default: { diff --git a/src/servers/sapphire_zone/Territory/Land.cpp b/src/servers/sapphire_zone/Territory/Land.cpp index d61dcaf5..4ef3bae8 100644 --- a/src/servers/sapphire_zone/Territory/Land.cpp +++ b/src/servers/sapphire_zone/Territory/Land.cpp @@ -45,7 +45,7 @@ Sapphire::Land::Land( uint16_t territoryTypeId, uint8_t wardNum, uint8_t landId, { memset( &m_tag, 0x00, 3 ); - load(); + init(); } Sapphire::Land::~Land() @@ -53,7 +53,7 @@ Sapphire::Land::~Land() } -void Sapphire::Land::load() +void Sapphire::Land::init() { auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); auto res = pDb->query( "SELECT * FROM land WHERE LandSetId = " + std::to_string( m_landSetId ) + " " @@ -98,7 +98,34 @@ void Sapphire::Land::load() m_mapMarkerPosition.z = info->z; } - init(); + switch( m_size ) + { + case HouseSize::Cottage: + m_maxPlacedExternalItems = 20; + m_maxPlacedInternalItems = 200; + break; + case HouseSize::House: + m_maxPlacedExternalItems = 30; + m_maxPlacedInternalItems = 300; + break; + case HouseSize::Mansion: + m_maxPlacedExternalItems = 40; + m_maxPlacedInternalItems = 400; + break; + default: + break; + } + + // init item containers + auto setupContainer = [ this ]( InventoryType type, uint8_t maxSize ) + { + m_landInventoryMap[ type ] = make_ItemContainer( type, maxSize, "houseiteminventory", true, true ); + }; + + setupContainer( InventoryType::HousingExternalAppearance, 8 ); + setupContainer( InventoryType::HousingInternalAppearance, 8 ); + setupContainer( InventoryType::HousingOutdoorItemStoreroom, m_maxPlacedExternalItems ); + setupContainer( InventoryType::HousingIndoorItemStoreroom, m_maxPlacedInternalItems ); } uint32_t Sapphire::Land::convertItemIdToHousingItemId( uint32_t itemId ) @@ -223,11 +250,6 @@ uint32_t Sapphire::Land::getPlayerOwner() return m_ownerPlayerId; } -uint32_t Sapphire::Land::getMaxItems() -{ - return m_maxItems; -} - uint32_t Sapphire::Land::getDevaluationTime() { return m_nextDrop - static_cast< uint32_t >( Util::getTimeSeconds() ); @@ -248,25 +270,6 @@ uint8_t Sapphire::Land::getLandTag( uint8_t slot ) return m_tag[ slot ]; } -void Sapphire::Land::init() -{ - - switch( m_size ) - { - case HouseSize::small: - m_maxItems = 20; - break; - case HouseSize::medium: - m_maxItems = 30; - break; - case HouseSize::big: - m_maxItems = 40; - break; - default: - break; - } -} - void Sapphire::Land::updateLandDb() { uint32_t houseId = 0; diff --git a/src/servers/sapphire_zone/Territory/Land.h b/src/servers/sapphire_zone/Territory/Land.h index 3720354a..1b4b2fa1 100644 --- a/src/servers/sapphire_zone/Territory/Land.h +++ b/src/servers/sapphire_zone/Territory/Land.h @@ -18,7 +18,7 @@ namespace Sapphire Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, Sapphire::Data::HousingLandSetPtr info ); virtual ~Land(); - void load(); + using LandInventoryMap = std::unordered_map< uint32_t, ItemContainerPtr >; //Primary state void setSize( uint8_t size ); @@ -53,7 +53,6 @@ namespace Sapphire void updateLandDb(); void update( uint32_t currTime ); - uint32_t getMaxItems(); uint32_t getCurrentPrice() const; uint32_t getMaxPrice() const; uint32_t getDevaluationTime(); @@ -87,8 +86,9 @@ namespace Sapphire Sapphire::HousePtr m_pHouse; //item storage - Sapphire::ItemContainerPtr ItemsOutdoorContainer; - uint32_t m_maxItems; + LandInventoryMap m_landInventoryMap; + uint32_t m_maxPlacedExternalItems; + uint32_t m_maxPlacedInternalItems; //price uint32_t m_initPrice;