diff --git a/src/common/Common.h b/src/common/Common.h index 9363426d..77d29367 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -866,13 +866,13 @@ namespace Sapphire::Common Mansion }; - enum HouseState : uint8_t + enum HouseStatus : uint8_t { none, - forSale, - sold, - privateHouse, - fcHouse, + ForSale, + Sold, + PrivateEstate, + FreeCompanyEstate, }; enum HouseIconAdd : uint8_t diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index ede6ed36..bca4b286 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -176,7 +176,7 @@ namespace Sapphire::Network::Packets DirectorVars = 0x01E1, // updated 4.4 - CFAvailableContents = 0x01FD, // updated 4.2 + CFAvailableContents = 0xF1FD, // updated 4.2 WeatherChange = 0x01FC, // updated 4.4 PlayerTitleList = 0x01FD, // updated 4.4 @@ -264,10 +264,10 @@ namespace Sapphire::Network::Packets LinkshellListHandler = 0x00F4, // updated 4.3 - MarketBoardRequestItemInformation = 0x00FE, // updated 4.4 + MarketBoardRequestItemListingInfo = 0x00FE, // updated 4.4 MarketBoardRequestItemListings = 0x00FF, // updated 4.4 + MarketBoardSearch = 0x0103, // updated 4.4 - SearchMarketboard = 0x0103, // updated 4.3 ReqExamineFcInfo = 0x010F, // updated 4.1 FcInfoReqHandler = 0x011A, // updated 4.2 diff --git a/src/common/Network/PacketDef/Zone/ClientZoneDef.h b/src/common/Network/PacketDef/Zone/ClientZoneDef.h index e096fa8a..f4a057ad 100644 --- a/src/common/Network/PacketDef/Zone/ClientZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ClientZoneDef.h @@ -237,8 +237,9 @@ struct FFXIVIpcSetSharedEstateSettings : struct FFXIVIpcMarketBoardRequestItemListings : FFXIVIpcBasePacket< MarketBoardRequestItemListings > { - /* 0000 */ uint32_t itemCatalogId; - /* 0004 */ uint32_t padding; + /* 0000 */ uint16_t padding1; + /* 0002 */ uint16_t itemCatalogId; + /* 0004 */ uint32_t padding2; }; struct FFXIVIpcReqPlaceHousingItem : @@ -270,6 +271,26 @@ struct FFXIVIpcHousingUpdateObjectPosition : /* 001C */ uint32_t padding; }; +struct FFXIVIpcMarketBoardSearch : + FFXIVIpcBasePacket< MarketBoardSearch > +{ + /* 0000 */ uint32_t startIdx; + /* 0004 */ uint16_t requestId; + /* 0006 */ uint8_t itemSearchCategory; + /* 0007 */ uint8_t shouldCheckClassJobId; // wat? seems only 1 there at least... + /* 0008 */ uint8_t maxEquipLevel; + /* 0009 */ uint8_t classJobId; + /* 000A */ char searchStr[40]; + /* 0032 */ uint16_t unk4[43]; +}; + +struct FFXIVIpcMarketBoardRequestItemListingInfo : + FFXIVIpcBasePacket< MarketBoardRequestItemListingInfo > +{ + /* 0000 */ uint32_t catalogId; + /* 0000 */ uint32_t requestId; +}; + } } } diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 63ea03ba..e1f3980e 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1847,23 +1847,24 @@ struct FFXIVIpcMarketBoardSearchResult : struct MarketBoardItem { uint32_t itemCatalogId; - uint32_t quantity; + uint16_t quantity; + uint16_t demand; } items[20]; uint32_t itemIndexEnd; uint32_t padding1; uint32_t itemIndexStart; - uint32_t padding2; + uint32_t requestId; }; struct FFFXIVIpcMarketBoardItemListingCount : FFXIVIpcBasePacket< MarketBoardItemListingCount > { - uint32_t itemCatalogId; - uint32_t unknown1; // does some shit if nonzero - uint16_t unknown2; - uint16_t quantity; // high/low u8s read separately? - uint32_t padding3; + uint32_t itemCatalogId; + uint32_t unknown1; // does some shit if nonzero + uint16_t requestId; + uint16_t quantity; // high/low u8s read separately? + uint32_t unknown3; }; struct FFXIVIpcMarketBoardItemListingHistory : @@ -1875,14 +1876,14 @@ struct FFXIVIpcMarketBoardItemListingHistory : struct MarketListing { uint32_t salePrice; - time_t purchaseTime; + uint32_t purchaseTime; uint32_t quantity; - uint16_t unknown1; - uint8_t unknown2; + uint8_t isHq; + uint8_t padding; + uint8_t onMannequin; - char sellerName[32]; + char buyerName[33]; - uint8_t unknown3; uint32_t itemCatalogId; } listing[20]; }; diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index 8badd385..5fda5ef4 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -179,8 +179,8 @@ void Sapphire::World::Manager::HousingMgr::initLandCache() entry.m_landId = res->getUInt( "LandId" ); entry.m_type = static_cast< Common::LandType >( res->getUInt( "Type" ) ); - entry.m_size = res->getUInt8( "Size" ); - entry.m_status = res->getUInt8( "Status" ); + entry.m_size = static_cast< Common::HouseSize >( res->getUInt8( "Size" ) ); + entry.m_status = static_cast< Common::HouseStatus >( res->getUInt8( "Status" ) ); entry.m_currentPrice = res->getUInt64( "LandPrice" ); entry.m_updateTime = res->getUInt64( "UpdateTime" ); entry.m_ownerId = res->getUInt64( "OwnerId" ); @@ -365,7 +365,7 @@ Sapphire::LandPurchaseResult Sapphire::World::Manager::HousingMgr::purchaseLand( if( !pLand ) return LandPurchaseResult::ERR_INTERNAL; - if( pLand->getState() != HouseState::forSale ) + if( pLand->getStatus() != HouseStatus::ForSale ) return LandPurchaseResult::ERR_NOT_AVAILABLE; if( gilAvailable < plotPrice ) @@ -388,7 +388,7 @@ Sapphire::LandPurchaseResult Sapphire::World::Manager::HousingMgr::purchaseLand( player.removeCurrency( CurrencyType::Gil, plotPrice ); pLand->setOwnerId( player.getId() ); - pLand->setState( HouseState::sold ); + pLand->setStatus( HouseStatus::Sold ); pLand->setLandType( Common::LandType::Private ); player.setLandFlags( LandFlagsSlot::Private, 0x00, pLand->getLandIdent() ); @@ -437,7 +437,7 @@ bool Sapphire::World::Manager::HousingMgr::relinquishLand( Entity::Player& playe pLand->setCurrentPrice( pLand->getMaxPrice() ); pLand->setOwnerId( 0 ); - pLand->setState( HouseState::forSale ); + pLand->setStatus( HouseStatus::ForSale ); pLand->setLandType( Common::LandType::none ); pLand->updateLandDb(); @@ -477,11 +477,11 @@ void Sapphire::World::Manager::HousingMgr::sendWardLandInfo( Entity::Player& pla auto& entry = wardInfoPacket->data().houseInfoEntry[ i ]; - // retail always sends the house price in this packet, even after the house has been sold + // retail always sends the house price in this packet, even after the house has been Sold // so I guess we do the same entry.housePrice = land->getCurrentPrice(); - if( land->getState() == Common::HouseState::forSale ) + if( land->getStatus() == Common::HouseStatus::ForSale ) continue; if( auto house = land->getHouse() ) @@ -568,7 +568,7 @@ bool Sapphire::World::Manager::HousingMgr::initHouseModels( Entity::Player& play if( !player.findFirstItemWithId( presetCatalogId, foundItem ) ) return false; - auto item = player.dropInventoryItem( foundItem.first, foundItem.second ); + auto item = getHousingItemFromPlayer( player, foundItem.first, foundItem.second ); if( !item ) return false; @@ -682,7 +682,7 @@ void Sapphire::World::Manager::HousingMgr::buildPresetEstate( Entity::Player& pl createHouse( house ); - pLand->setState( HouseState::privateHouse ); + pLand->setStatus( HouseStatus::PrivateEstate ); pLand->setLandType( LandType::Private ); hZone->sendLandUpdate( plotNum ); @@ -1002,12 +1002,10 @@ void Sapphire::World::Manager::HousingMgr::reqPlaceHousingItem( Sapphire::Entity containerId == InventoryType::Bag2 || containerId == InventoryType::Bag3 ) { - auto tmpItem = player.dropInventoryItem( static_cast< Common::InventoryType >( containerId ), slotId ); - if( !tmpItem ) + item = getHousingItemFromPlayer( player, static_cast< Common::InventoryType >( containerId ), slotId ); + if( !item ) return; - item = Inventory::make_HousingItem( tmpItem->getUId(), tmpItem->getId(), framework() ); - // set params item->setPos( { Util::floatToUInt16( pos.x ), @@ -1074,7 +1072,7 @@ void Sapphire::World::Manager::HousingMgr::reqPlaceItemInStore( Sapphire::Entity if( freeSlot == -1 ) return; - auto item = player.dropInventoryItem( static_cast< Common::InventoryType >( containerId ), slotId ); + auto item = getHousingItemFromPlayer( player, static_cast< Common::InventoryType >( containerId ), slotId ); if( !item ) return; @@ -1097,7 +1095,7 @@ void Sapphire::World::Manager::HousingMgr::reqPlaceItemInStore( Sapphire::Entity continue; } - auto item = player.dropInventoryItem( static_cast< Common::InventoryType >( containerId ), slotId ); + auto item = getHousingItemFromPlayer( player, static_cast< Common::InventoryType >( containerId ), slotId ); if( !item ) return; @@ -1386,24 +1384,24 @@ bool Sapphire::World::Manager::HousingMgr::removeInternalItem( Entity::Player& p { auto& containers = getEstateInventory( terri.getLandIdent() ); - // validate the container id first - // we also need the idx of the container so we can get the slot offset - bool foundContainer = false; - uint8_t containerIdx = 0; - for( auto cId : m_internalPlacedItemContainers ) + int8_t containerIdx = 0; + + if( isPlacedItemsInventory( static_cast< Common::InventoryType >( containerId ) ) ) { - if( containerId == cId ) + for( auto cId : m_internalPlacedItemContainers ) { - foundContainer = true; + if( containerId == cId ) + break; - break; + containerIdx++; } - - containerIdx++; } + else + containerIdx = -1; - if( !foundContainer ) - return false; + // its possible to remove an item from any container in basically all these remove functions + // eg, remove a permit and reuse it elsewhere + // I'm not going to bother fixing it for now, but worth noting for future reference auto needle = containers.find( containerId ); if( needle == containers.end() ) @@ -1457,8 +1455,11 @@ bool Sapphire::World::Manager::HousingMgr::removeInternalItem( Entity::Player& p } // despawn - auto arraySlot = ( containerIdx * 50 ) + slotId; - terri.removeHousingObject( arraySlot ); + if( containerIdx != -1 ) + { + auto arraySlot = ( containerIdx * 50 ) + slotId; + terri.removeHousingObject( arraySlot ); + } return true; } @@ -1614,4 +1615,14 @@ bool Sapphire::World::Manager::HousingMgr::hasPermission( Sapphire::Entity::Play // todo: check perms here return false; +} + +Sapphire::Inventory::HousingItemPtr Sapphire::World::Manager::HousingMgr::getHousingItemFromPlayer( + Entity::Player& player, Common::InventoryType type, uint8_t slot ) +{ + auto tmpItem = player.dropInventoryItem( type, slot ); + if( !tmpItem ) + return nullptr; + + return Inventory::make_HousingItem( tmpItem->getUId(), tmpItem->getId(), framework() ); } \ No newline at end of file diff --git a/src/world/Manager/HousingMgr.h b/src/world/Manager/HousingMgr.h index deb6c099..8167efb9 100644 --- a/src/world/Manager/HousingMgr.h +++ b/src/world/Manager/HousingMgr.h @@ -31,8 +31,8 @@ namespace Sapphire::World::Manager uint16_t m_landId; Common::LandType m_type; - uint8_t m_size; - uint8_t m_status; + Common::HouseSize m_size; + Common::HouseStatus m_status; uint64_t m_currentPrice; @@ -185,6 +185,8 @@ namespace Sapphire::World::Manager private: + Inventory::HousingItemPtr getHousingItemFromPlayer( Entity::Player& player, Common::InventoryType type, uint8_t slot ); + ItemContainerPtr getFreeEstateInventorySlot( Common::LandIdent ident, Inventory::InventoryContainerPair& pair, Inventory::InventoryTypeList bagList ); diff --git a/src/world/Manager/MarketMgr.cpp b/src/world/Manager/MarketMgr.cpp new file mode 100644 index 00000000..d18d28e1 --- /dev/null +++ b/src/world/Manager/MarketMgr.cpp @@ -0,0 +1,159 @@ +#include "MarketMgr.h" + +#include +#include +#include + +#include +#include +#include + +#include "Actor/Player.h" + +#include + +using namespace Sapphire::Network::Packets; + +Sapphire::World::Manager::MarketMgr::MarketMgr( Sapphire::FrameworkPtr pFw ) : + BaseManager( pFw ) +{ + +} + +bool Sapphire::World::Manager::MarketMgr::init() +{ +// Logger::info( "MarketMgr: warming up marketable item cache..." ); +// +// // build item cache +// auto exdData = framework()->get< Sapphire::Data::ExdDataGenerated >(); +// auto idList = exdData->getItemIdList(); +// +// for( auto id : idList ) +// { +// if( id > 10000 ) +// break; +// +// auto item = exdData->get< Sapphire::Data::Item >( id ); +// if( !item ) +// continue; +// +// if( item->isUntradable ) +// continue; +// +// MarketableItem cacheEntry {}; +// cacheEntry.catalogId = id; +// cacheEntry.itemSearchCategory = item->itemSearchCategory; +// cacheEntry.maxEquipLevel = item->levelEquip; +// cacheEntry.name = item->name; +// cacheEntry.classJob = item->classJobUse; +// cacheEntry.itemLevel = item->levelItem; +// +// m_marketItemCache.push_back( std::move( cacheEntry ) ); +// } +// +// std::sort( m_marketItemCache.begin(), m_marketItemCache.end(), []( const MarketableItem& a, const MarketableItem& b ) +// { +// return a.itemLevel > b.itemLevel; +// } ); +// +// Logger::info( "MarketMgr: Cached " + std::to_string( m_marketItemCache.size() ) + " marketable items" ); + + return true; +} + +void Sapphire::World::Manager::MarketMgr::requestItemListingInfo( Sapphire::Entity::Player& player, uint32_t catalogId, + uint32_t requestId ) +{ + auto countPkt = makeZonePacket< Server::FFFXIVIpcMarketBoardItemListingCount >( player.getId() ); + countPkt->data().quantity = 1 << 8; + countPkt->data().itemCatalogId = catalogId; + countPkt->data().requestId = requestId; + + player.queuePacket( countPkt ); + + auto historyPkt = makeZonePacket< Server::FFXIVIpcMarketBoardItemListingHistory >( player.getId() ); + historyPkt->data().itemCatalogId = catalogId; + historyPkt->data().itemCatalogId2 = catalogId; + + std::string name = "fix game pls se :((("; + + for( int i = 0; i < 10; i++ ) + { + auto& listing = historyPkt->data().listing[ i ]; + + listing.itemCatalogId = catalogId; + listing.quantity = i + 1; + listing.purchaseTime = time( nullptr ); + listing.salePrice = 69420420; + listing.isHq = 1; + listing.onMannequin = 1; + + + strcpy( listing.buyerName, name.c_str() ); + } + + player.queuePacket( historyPkt ); +} + + +void Sapphire::World::Manager::MarketMgr::searchMarketboard( Entity::Player& player, uint8_t itemSearchCategory, + uint8_t maxEquipLevel, uint8_t classJob, + const std::string_view& searchStr, uint32_t requestId, + uint32_t startIdx ) +{ + ItemSearchResultList resultList; + findItems( searchStr, itemSearchCategory, maxEquipLevel, classJob, resultList ); + + auto numResults = resultList.size(); + + if( startIdx > numResults ) + return; + + auto endIdx = std::min< size_t >( startIdx + 20, numResults ); + auto size = endIdx - startIdx; + + auto resultPkt = makeZonePacket< Server::FFXIVIpcMarketBoardSearchResult >( player.getId() ); + resultPkt->data().itemIndexStart = startIdx; + resultPkt->data().requestId = requestId; + + for( auto i = 0; i < size; i++ ) + { + auto& item = resultList.at( startIdx + i ); + auto& data = resultPkt->data().items[ i ]; + + data.itemCatalogId = item.catalogId; + data.quantity = item.quantity; + data.demand = 420; + } + + if( size < 20 ) + resultPkt->data().itemIndexEnd = 0; + else + resultPkt->data().itemIndexEnd = startIdx + 20; + + player.queuePacket( resultPkt ); +} + +void Sapphire::World::Manager::MarketMgr::requestItemListings( Sapphire::Entity::Player& player, uint16_t catalogId ) +{ + +} + +void Sapphire::World::Manager::MarketMgr::findItems( const std::string_view& searchStr, uint8_t itemSearchCat, + uint8_t maxEquipLevel, uint8_t classJob, + Sapphire::World::Manager::MarketMgr::ItemSearchResultList& resultList ) +{ + for( const auto& item : m_marketItemCache ) + { + if( item.itemSearchCategory != itemSearchCat ) + continue; + + if( maxEquipLevel > 0 && item.maxEquipLevel > maxEquipLevel ) + continue; + + if( classJob > 0 && item.classJob != classJob ) + continue; + + resultList.push_back( { item.catalogId, 1 } ); + } +} \ No newline at end of file diff --git a/src/world/Manager/MarketMgr.h b/src/world/Manager/MarketMgr.h new file mode 100644 index 00000000..328c9c12 --- /dev/null +++ b/src/world/Manager/MarketMgr.h @@ -0,0 +1,58 @@ +#ifndef SAPPHIRE_MARKETMGR_H +#define SAPPHIRE_MARKETMGR_H + +#include "ForwardsZone.h" +#include "BaseManager.h" + +#include + +namespace Sapphire::World::Manager +{ + class MarketMgr : public Manager::BaseManager + { + public: + explicit MarketMgr( FrameworkPtr pFw ); + + bool init(); + + void searchMarketboard( Entity::Player& player, uint8_t itemSearchCategory, + uint8_t maxEquipLevel, uint8_t classJob, + const std::string_view& searchStr, uint32_t requestId, + uint32_t startIdx ); + + void requestItemListingInfo( Entity::Player& player, uint32_t catalogId, uint32_t requestId ); + + void requestItemListings( Entity::Player& player, uint16_t catalogId ); + + private: + struct ItemSearchResult + { + uint32_t catalogId; + uint16_t quantity; + }; + + struct MarketableItem + { + uint32_t catalogId; + uint8_t itemSearchCategory; + uint8_t maxEquipLevel; + uint16_t itemLevel; + uint8_t classJob; + std::string name; + }; + + using ItemSearchResultList = std::vector< ItemSearchResult >; + using MarketableItemCacheList = std::vector< MarketableItem >; + + MarketableItemCacheList m_marketItemCache; + + + + void findItems( const std::string_view& searchStr, uint8_t itemSearchCat, uint8_t maxEquipLevel, uint8_t classJob, + ItemSearchResultList& resultList ); + + }; +} + + +#endif //SAPPHIRE_MARKETMGR_H diff --git a/src/world/Manager/PlayerMgr.h b/src/world/Manager/PlayerMgr.h index 0b0cbd13..f7d08488 100644 --- a/src/world/Manager/PlayerMgr.h +++ b/src/world/Manager/PlayerMgr.h @@ -1,3 +1,6 @@ +#ifndef SAPPHIRE_PLAYERMGR_H +#define SAPPHIRE_PLAYERMGR_H + #include "ForwardsZone.h" #include "BaseManager.h" @@ -10,4 +13,6 @@ class PlayerMgr : public Manager::BaseManager void movePlayerToLandDestination( Sapphire::Entity::Player& player, uint32_t landId, uint16_t param = 0 ); }; -} \ No newline at end of file +} + +#endif // SAPPHIRE_PLAYERMGR_H \ No newline at end of file diff --git a/src/world/Network/GameConnection.cpp b/src/world/Network/GameConnection.cpp index 8761647a..57e336f4 100644 --- a/src/world/Network/GameConnection.cpp +++ b/src/world/Network/GameConnection.cpp @@ -123,6 +123,12 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH setZoneHandler( ClientZoneIpcType::PerformNoteHandler, "PerformNoteHandler", &GameConnection::performNoteHandler ); + setZoneHandler( ClientZoneIpcType::MarketBoardSearch, "MarketBoardSearch", &GameConnection::marketBoardSearch ); + setZoneHandler( ClientZoneIpcType::MarketBoardRequestItemListingInfo, "MarketBoardRequestItemListingInfo", + &GameConnection::marketBoardRequestItemInfo ); + setZoneHandler( ClientZoneIpcType::MarketBoardRequestItemListings, "MarketBoardRequestItemListings", + &GameConnection::marketBoardRequestItemListings ); + setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler ); } diff --git a/src/world/Network/GameConnection.h b/src/world/Network/GameConnection.h index dcdec9d1..72c800b2 100644 --- a/src/world/Network/GameConnection.h +++ b/src/world/Network/GameConnection.h @@ -176,6 +176,12 @@ namespace Sapphire::Network DECLARE_HANDLER( reqMoveHousingItem ); + DECLARE_HANDLER( marketBoardSearch ); + + DECLARE_HANDLER( marketBoardRequestItemInfo ); + + DECLARE_HANDLER( marketBoardRequestItemListings ); + }; } diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index fa6543bf..d737bf8a 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -36,6 +36,7 @@ #include "Manager/DebugCommandMgr.h" #include "Manager/EventMgr.h" +#include "Manager/MarketMgr.h" #include "Action/Action.h" #include "Action/ActionTeleport.h" @@ -752,4 +753,41 @@ void Sapphire::Network::GameConnection::reqMoveHousingItem( FrameworkPtr pFw, housingMgr->reqMoveHousingItem( player, data.ident, data.slot, data.pos, data.rotation ); +} + +void Sapphire::Network::GameConnection::marketBoardSearch( FrameworkPtr pFw, + const Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + auto marketMgr = pFw->get< MarketMgr >(); + + const auto packet = ZoneChannelPacket< Client::FFXIVIpcMarketBoardSearch >( inPacket ); + const auto& data = packet.data(); + + std::string_view searchStr( data.searchStr ); + + marketMgr->searchMarketboard( player, data.itemSearchCategory, data.maxEquipLevel, data.classJobId, data.searchStr, + data.requestId, data.startIdx ); +} + +void Sapphire::Network::GameConnection::marketBoardRequestItemInfo( FrameworkPtr pFw, + const Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + const auto packet = ZoneChannelPacket< Client::FFXIVIpcMarketBoardRequestItemListingInfo >( inPacket ); + + auto marketMgr = pFw->get< MarketMgr >(); + + marketMgr->requestItemListingInfo( player, packet.data().catalogId, packet.data().requestId ); +} + +void Sapphire::Network::GameConnection::marketBoardRequestItemListings( FrameworkPtr pFw, + const Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + const auto packet = ZoneChannelPacket< Client::FFXIVIpcMarketBoardRequestItemListings >( inPacket ); + + auto marketMgr = pFw->get< MarketMgr >(); + + marketMgr->requestItemListings( player, packet.data().itemCatalogId ); } \ No newline at end of file diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index cc89386a..316f6460 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -39,6 +39,7 @@ #include "Manager/InventoryMgr.h" #include "Manager/EventMgr.h" #include "Manager/ItemMgr.h" +#include "Manager/MarketMgr.h" using namespace Sapphire::World::Manager; @@ -160,6 +161,15 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] ) return; } + auto pMarketMgr = std::make_shared< Manager::MarketMgr >( framework() ); + framework()->set< Manager::MarketMgr >( pMarketMgr ); + + if( !pMarketMgr->init() ) + { + Logger::fatal( "Failed to setup market manager!" ); + return; + } + loadBNpcTemplates(); Network::HivePtr hive( new Network::Hive() ); diff --git a/src/world/Territory/Housing/HousingInteriorTerritory.cpp b/src/world/Territory/Housing/HousingInteriorTerritory.cpp index 63fb581e..6bef748b 100644 --- a/src/world/Territory/Housing/HousingInteriorTerritory.cpp +++ b/src/world/Territory/Housing/HousingInteriorTerritory.cpp @@ -76,14 +76,19 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onPlayerZone player.queuePacket( indoorInitPacket ); + bool isFcHouse = pLand->getStatus() == Common::HouseStatus::PrivateEstate; auto yardPacketTotal = static_cast< uint8_t >( 2 + pLand->getSize() ); for( uint8_t yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) { auto objectInitPacket = makeZonePacket< Server::FFXIVIpcHousingObjectInitialize >( player.getId() ); memcpy( &objectInitPacket->data().landIdent, &m_landIdent, sizeof( Common::LandIdent ) ); - // todo: change this when FC houses become a thing - objectInitPacket->data().u1 = 2; // 2 = actrl 0x400 will hide the fc door, otherwise it will stay there + + if( isFcHouse ) + objectInitPacket->data().u1 = 2; // 2 = actrl 0x400 will hide the fc door, otherwise it will stay there + else + objectInitPacket->data().u1 = 0; + objectInitPacket->data().u2 = 100; objectInitPacket->data().packetNum = yardPacketNum; objectInitPacket->data().packetTotal = yardPacketTotal; @@ -94,8 +99,8 @@ void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onPlayerZone player.queuePacket( objectInitPacket ); } - // todo: if in fc house, don't send this - player.queuePacket( Server::makeActorControl143( player.getId(), Network::ActorControl::HideAdditionalChambersDoor ) ); + if( isFcHouse ) + player.queuePacket( Server::makeActorControl143( player.getId(), Network::ActorControl::HideAdditionalChambersDoor ) ); } void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime ) diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index 3814b471..9f79defa 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -220,7 +220,7 @@ void Sapphire::HousingZone::sendLandSet( Entity::Player& player ) auto& landData = landsetInitializePacket->data().land[ count ]; landData.plotSize = pLand->getSize(); - landData.houseState = pLand->getState(); + landData.houseState = pLand->getStatus(); landData.iconAddIcon = pLand->getSharing(); landData.fcId = pLand->getFcId(); landData.fcIcon = pLand->getFcIcon(); @@ -256,7 +256,7 @@ void Sapphire::HousingZone::sendLandUpdate( uint8_t landId ) auto& landData = landUpdatePacket->data().land; landData.plotSize = pLand->getSize(); - landData.houseState = pLand->getState(); + landData.houseState = pLand->getStatus(); landData.iconAddIcon = pLand->getSharing(); landData.fcId = pLand->getFcId(); landData.fcIcon = pLand->getFcIcon(); diff --git a/src/world/Territory/Land.cpp b/src/world/Territory/Land.cpp index 432b473b..15411d24 100644 --- a/src/world/Territory/Land.cpp +++ b/src/world/Territory/Land.cpp @@ -54,7 +54,7 @@ Sapphire::Land::Land( uint16_t territoryTypeId, uint8_t wardNum, uint8_t landId, Sapphire::Land::~Land() = default; -void Sapphire::Land::init( Common::LandType type, uint8_t size, uint8_t state, uint32_t currentPrice, +void Sapphire::Land::init( Common::LandType type, Common::HouseSize size, Common::HouseStatus state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId ) { m_type = type; @@ -84,12 +84,12 @@ uint32_t Sapphire::Land::getMaxPrice() const } //Primary State -void Sapphire::Land::setSize( uint8_t size ) +void Sapphire::Land::setSize( Common::HouseSize size ) { m_size = size; } -void Sapphire::Land::setState( uint8_t state ) +void Sapphire::Land::setStatus( Common::HouseStatus state ) { m_state = state; } @@ -104,12 +104,12 @@ void Sapphire::Land::setLandType( Common::LandType type ) m_type = type; } -uint8_t Sapphire::Land::getSize() const +Sapphire::Common::HouseSize Sapphire::Land::getSize() const { return m_size; } -uint8_t Sapphire::Land::getState() const +Sapphire::Common::HouseStatus Sapphire::Land::getStatus() const { return m_state; } @@ -227,7 +227,7 @@ void Sapphire::Land::updateLandDb() void Sapphire::Land::update( uint32_t currTime ) { - if( getState() == HouseState::forSale ) + if( getStatus() == HouseStatus::ForSale ) { if( m_nextDrop < currTime && m_minPrice < m_currentPrice ) { diff --git a/src/world/Territory/Land.h b/src/world/Territory/Land.h index f6af5f2a..8eecbb33 100644 --- a/src/world/Territory/Land.h +++ b/src/world/Territory/Land.h @@ -18,20 +18,20 @@ namespace Sapphire Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, Sapphire::Data::HousingLandSetPtr info, FrameworkPtr pFw ); virtual ~Land(); - void init( Common::LandType type, uint8_t size, uint8_t state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId ); + void init( Common::LandType type, Common::HouseSize size, Common::HouseStatus state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId ); using LandInventoryMap = std::unordered_map< uint16_t, ItemContainerPtr >; using InvMaxItemsPair = std::pair< uint16_t, uint16_t >; //Primary state - void setSize( uint8_t size ); - void setState( uint8_t state ); + void setSize( Common::HouseSize size ); + void setStatus( Common::HouseStatus state ); void setSharing( uint8_t state ); void setLandType( Common::LandType type ); //Gerneral - uint8_t getSize() const; - uint8_t getState() const; + Common::HouseSize getSize() const; + Common::HouseStatus getStatus() const; uint8_t getSharing() const; uint32_t getLandSetId() const; Common::LandType getLandType() const; @@ -71,8 +71,8 @@ namespace Sapphire Common::LandIdent m_landIdent; uint32_t m_landSetId; - uint8_t m_size; - uint8_t m_state; + Common::HouseSize m_size; + Common::HouseStatus m_state; Common::LandType m_type; uint8_t m_iconAddIcon; uint32_t m_fcId; // unclear, may be wrong