diff --git a/sql/update_land.sql b/sql/update_land.sql index 87f14e23..7dc864f6 100644 --- a/sql/update_land.sql +++ b/sql/update_land.sql @@ -1,4 +1,5 @@ ALTER TABLE `land` ADD `Type` SMALLINT(6) NOT NULL DEFAULT '0' AFTER `LandId`; ALTER TABLE `house` ADD `HouseName` binary(23) DEFAULT "" AFTER `Comment`; ALTER TABLE `house` ADD `HousePartModels` BINARY(32) DEFAULT "" AFTER `Endorsements`; -ALTER TABLE `house` ADD `HousePartColours` BINARY(8) DEFAULT "" AFTER `HousePartModels`; \ No newline at end of file +ALTER TABLE `house` ADD `HousePartColours` BINARY(8) DEFAULT "" AFTER `HousePartModels`; +ALTER TABLE `house` ADD `HouseInteriorModels` BINARY(40) DEFAULT "" AFTER `HousePartColours`; \ No newline at end of file diff --git a/src/common/Common.h b/src/common/Common.h index 0eaa4b4f..2ed618a3 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -751,6 +751,20 @@ namespace Sapphire::Common YardSign }; + enum HousingInteriorSlot + { + InteriorWall, + InteriorFloor, + InteriorLight, + InteriorWall_Attic, + InteriorFloor_Attic, + InteriorLight_Attic, + InteriorWall_Basement, + InteriorFloor_Basement, + InteriorLight_Basement, + InteriorLight_Mansion + }; + enum HouseTagSlot { MainTag, diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index 0cc56879..28eba502 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -197,7 +197,7 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() CONNECTION_BOTH ); prepareStatement( HOUSING_HOUSE_UP, - "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ?, HousePartModels = ?, HousePartColours = ? WHERE HouseId = ?;", + "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ?, HousePartModels = ?, HousePartColours = ?, HouseInteriorModels = ? WHERE HouseId = ?;", CONNECTION_BOTH ); /*prepareStatement( LAND_INS, diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 357bb54b..e15c95c3 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -207,23 +207,38 @@ enum ActorControlType : uint16_t Dismount = 0x3A0, // Duty Recorder - BeginReplayAck = 0x3A1, + BeginReplayAck = 0x3A1, EndReplayAck = 0x3A2, // Housing - ShowHousingItemUI = 0x3F7, + ShowHousingItemUI = 0x3F7, ShowBuildPresetUI = 0x3E9, BuildPresetResponse = 0x3ED, + /*! + * param1 = outdoor furnishings + * u8 0 - relocation available, 1 = available + * u8 1 - outoor furnishings placed + * u8 2 - outdoor furnishings in storeroom + * u8 3 - outdoor funishings limit + * param2 = indoor furnishings + * u16 0 - relocation available, 1 = available + * u16 1 - furnishings placed + * param3 = indoor furnishings + * u16 0 - in storeroom + * u16 1 - indoor furnishings limit + */ + HousingStoreroomStatus = 0x419, + // PvP Duel - SetPvPState = 0x5E0, // param3 must be 6 to engage a duel (hardcoded in the client) + SetPvPState = 0x5E0, // param3 must be 6 to engage a duel (hardcoded in the client) EndDuelSession = 0x5E1, // because someone went oob? StartDuelCountdown = 0x5E2, // begins a countdown; also does some duel bgm thing. StartDuel = 0x5E3, // actually all it does is set the challenger actor id; DuelResultScreen = 0x5E4, // win/lose thing, also reset a target id just like what EndDuelSession does. // Duty Action - SetDutyActionId = 0x5E8, // ContentExAction + SetDutyActionId = 0x5E8, // ContentExAction SetDutyActionHud = 0x5E9, // disable/enable SetDutyActionActive = 0x5EA, SetDutyActionRemaining = 0x5EB, @@ -304,6 +319,7 @@ enum ActorControlType : uint16_t RequestHousingItemUI = 0x463, RequestSharedEstateSettings = 0x46F, + UpdateEstateLightingLevel = 0x471, CompanionAction = 0x6A4, CompanionSetBarding = 0x6A5, diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 298facb7..51a4c4bf 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -97,10 +97,15 @@ namespace Sapphire::Network::Packets ReqMoogleMailLetter = 0x011A, // updated 4.4 MailLetterNotification = 0x011B, // updated 4.4 - ExamineFreeCompanyInfo = 0x013A, // updated 4.1 + MarketBoardItemListingCount = 0x011C, // updated 4.4 + MarketBoardItemListing = 0x011D, // updated 4.4 + MarketBoardItemListingHistory = 0x0121, // updated 4.4 + MarketBoardSearchResult = 0x0125, // updated 4.4 + CharaFreeCompanyTag = 0x0127, // updated 4.4 FreeCompanyBoardMsg = 0x0128, // updated 4.4 FreeCompanyInfo = 0x0129, // updated 4.4 + ExamineFreeCompanyInfo = 0x013A, // updated 4.1 StatusEffectList = 0x0149, // updated 4.4 Effect = 0x014C, // updated 4.4 @@ -186,7 +191,7 @@ namespace Sapphire::Network::Packets LandSetInitialize = 0x0220, // updated 4.4 LandUpdate = 0x0221, // updated 4.4 YardObjectSpawn = 0x0222, // updated 4.4 - + HousingIndoorInitialize = 0x0223, LandPriceUpdate = 0x0224, // updated 4.4 LandInfoSign = 0x0225, // updated 4.4 LandRename = 0x0226, // updated 4.4 @@ -195,7 +200,7 @@ namespace Sapphire::Network::Packets HousingLandFlags = 0x0229, // updated 4.4 HousingShowEstateGuestAccess = 0x022A, // updated 4.4 - LandSetYardInitialize = 0x022C, // updated 4.4 + HousingObjectInitialize = 0x022C, // updated 4.4 HousingWardInfo = 0x022F, // updated 4.4 YardObjectMove = 0x0230, // updated 4.4 @@ -258,6 +263,9 @@ namespace Sapphire::Network::Packets LinkshellListHandler = 0x00F4, // updated 4.3 + MarketBoardRequestItemInformation = 0x00FE, // updated 4.4 + MarketBoardRequestItemListings = 0x00FF, // updated 4.4 + SearchMarketboard = 0x0103, // updated 4.3 ReqExamineFcInfo = 0x010F, // updated 4.1 diff --git a/src/common/Network/PacketDef/Zone/ClientZoneDef.h b/src/common/Network/PacketDef/Zone/ClientZoneDef.h index 07fc7d88..b2b33033 100644 --- a/src/common/Network/PacketDef/Zone/ClientZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ClientZoneDef.h @@ -233,6 +233,13 @@ struct FFXIVIpcSetSharedEstateSettings : /* 0029 */ char padding3[0x7]; }; +struct FFXIVIpcMarketBoardRequestItemListings : + FFXIVIpcBasePacket< MarketBoardRequestItemListings > +{ + /* 0000 */ uint32_t itemCatalogId; + /* 0004 */ uint32_t padding; +}; + } } } diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 1e8efb03..2db26254 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1704,17 +1704,27 @@ struct FFXIVIpcYardObjectMove : FFXIVIpcBasePacket uint16_t unknown3; }; -struct FFXIVIpcLandSetYardInitialize : FFXIVIpcBasePacket< LandSetYardInitialize > +struct FFXIVIpcHousingObjectInitialize : FFXIVIpcBasePacket< HousingObjectInitialize > { - uint32_t unknown1; //always 0xFFFFFFFF - uint32_t unknown2; //always 0xFFFFFFFF - uint8_t unknown3; //always 0xFF + Common::LandIdent landIdent; + int8_t u1; //Outdoor -1 / Indoor 0 - probably indicator uint8_t packetNum; - uint16_t packetTotal; + uint8_t packetTotal; + uint8_t u2; //Outdoor 0 / Indoor 100(?) Common::YardObject object[100]; uint32_t unknown4; //unused }; +struct FFXIVIpcHousingIndoorInitialize : FFXIVIpcBasePacket< HousingIndoorInitialize > +{ + uint16_t u1; + uint16_t u2; + uint16_t u3; + uint16_t u4; + uint32_t indoorItems[10]; +}; + + struct FFXIVIpcHousingWardInfo : FFXIVIpcBasePacket< HousingWardInfo > { Common::LandIdent landIdent; @@ -1820,6 +1830,52 @@ struct FFXIVIpcDuelChallenge : char otherName[32]; }; +struct FFXIVIpcMarketBoardSearchResult : + FFXIVIpcBasePacket< MarketBoardSearchResult > +{ + struct MarketBoardItem + { + uint32_t itemCatalogId; + uint32_t quantity; + } items[20]; + + uint32_t itemIndexEnd; + uint32_t padding1; + uint32_t itemIndexStart; + uint32_t padding2; +}; + +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; +}; + +struct FFXIVIpcMarketBoardItemListingHistory : + FFXIVIpcBasePacket< MarketBoardItemListingHistory > +{ + uint32_t itemCatalogId; + uint32_t itemCatalogId2; + + struct MarketListing + { + uint32_t salePrice; + time_t purchaseTime; + uint32_t quantity; + uint16_t unknown1; + uint8_t unknown2; + + char sellerName[32]; + + uint8_t unknown3; + uint32_t itemCatalogId; + } listing[20]; +}; + } /* Server */ } /* Packets */ diff --git a/src/servers/Scripts/common/eobj/HousingEstateEntrance.cpp b/src/servers/Scripts/common/eobj/HousingEstateEntrance.cpp index 6980d2f6..a576bc19 100644 --- a/src/servers/Scripts/common/eobj/HousingEstateEntrance.cpp +++ b/src/servers/Scripts/common/eobj/HousingEstateEntrance.cpp @@ -23,6 +23,10 @@ public: player.playScene( eventId, 0, 0, [this, eobj]( Entity::Player& player, const Event::SceneResult& result ) { + // param2 == 1 when player wants to enter house + if( result.param2 != 1 ) + return; + auto terriMgr = getFramework()->get< Sapphire::World::Manager::TerritoryMgr >(); if( !terriMgr ) return; @@ -35,6 +39,7 @@ public: ident.landId = eobj.getHousingLink() >> 8; ident.territoryTypeId = zone->getTerritoryTypeId(); ident.wardNum = zone->getWardNum(); + ident.worldId = 67; auto internalZone = terriMgr->findOrCreateHousingInterior( ident ); if( internalZone ) @@ -42,10 +47,10 @@ public: player.sendDebug( "created zone with guid: " + std::to_string( internalZone->getGuId() ) + "\nname: " + internalZone->getName() ); } - if( result.param2 != 1 ) - return; + player.eventFinish( result.eventId, 1 ); - // param2 == 1, zone into instance + player.setPos( { 0.f, 0.f, 0.f } ); + player.setInstance( internalZone ); } ); } }; \ No newline at end of file diff --git a/src/servers/Scripts/common/housing/CmnDefHousingPersonalRoomEntrance.cpp b/src/servers/Scripts/common/housing/CmnDefHousingPersonalRoomEntrance.cpp new file mode 100644 index 00000000..abb5e740 --- /dev/null +++ b/src/servers/Scripts/common/housing/CmnDefHousingPersonalRoomEntrance.cpp @@ -0,0 +1,21 @@ +#include +#include + +using namespace Sapphire; + +class CmnDefHousingPersonalRoomEntrance : public Sapphire::ScriptAPI::EventScript +{ +public: + CmnDefHousingPersonalRoomEntrance() : + Sapphire::ScriptAPI::EventScript( 0x000b00b2 ) + { + } + + void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override + { + player.playScene( eventId, 0, 0, []( Entity::Player& player, const Event::SceneResult& result ) + { + + } ); + } +}; \ No newline at end of file diff --git a/src/servers/Scripts/common/CmnDefHousingSignboard.cpp b/src/servers/Scripts/common/housing/CmnDefHousingSignboard.cpp similarity index 73% rename from src/servers/Scripts/common/CmnDefHousingSignboard.cpp rename to src/servers/Scripts/common/housing/CmnDefHousingSignboard.cpp index 650baee8..8b0ba8e8 100644 --- a/src/servers/Scripts/common/CmnDefHousingSignboard.cpp +++ b/src/servers/Scripts/common/housing/CmnDefHousingSignboard.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "Framework.h" @@ -47,37 +48,32 @@ public: { auto screenMsgPkt = makeActorControl143( player.getId(), ActorControl::DutyQuestScreenMsg, m_id, 0x98 ); player.queuePacket( screenMsgPkt ); - auto screenMsgPkt2 = makeActorControl143( player.getId(), ActorControl::LogMsg, 0x0D16, 0x1AA, - activeLand.ward + 1, activeLand.plot + 1 ); - player.queuePacket( screenMsgPkt2 ); + + player.sendLogMessage( 0x0D16, pTerritory->getTerritoryTypeInfo()->placeName, activeLand.ward + 1, activeLand.plot + 1 ); break; } case LandPurchaseResult::ERR_NOT_ENOUGH_GIL: { - auto errorMsg = makeActorControl143( player.getId(), ActorControl::LogMsg, 3314 ); - player.queuePacket( errorMsg ); + player.sendLogMessage( 3314 ); break; } case LandPurchaseResult::ERR_NOT_AVAILABLE: { - auto errorMsg = makeActorControl143( player.getId(), ActorControl::LogMsg, 3312 ); - player.queuePacket( errorMsg ); + player.sendLogMessage( 3312 ); break; } case LandPurchaseResult::ERR_NO_MORE_LANDS_FOR_CHAR: { - auto errorMsg = makeActorControl143( player.getId(), ActorControl::LogMsg, 3313 ); - player.queuePacket( errorMsg ); + player.sendLogMessage( 3313 ); break; } case LandPurchaseResult::ERR_INTERNAL: { - auto errorMsg = makeActorControl143( player.getId(), ActorControl::LogMsg, 1995 ); - player.queuePacket( errorMsg ); + player.sendLogMessage( 1995 ); break; } } diff --git a/src/servers/Scripts/common/warptaxi/HousingWarpTaxiExitEstate.cpp b/src/servers/Scripts/common/warptaxi/HousingWarpTaxiExitEstate.cpp new file mode 100644 index 00000000..5dd34bf3 --- /dev/null +++ b/src/servers/Scripts/common/warptaxi/HousingWarpTaxiExitEstate.cpp @@ -0,0 +1,25 @@ +#include +#include + +using namespace Sapphire; + +class HousingWarpTaxiExitEstate : public Sapphire::ScriptAPI::EventScript +{ +public: + HousingWarpTaxiExitEstate() : + Sapphire::ScriptAPI::EventScript( 0x0002004d ) + { + } + + void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override + { + player.playScene( eventId, 0, 0, []( Entity::Player& player, const Event::SceneResult& result ) + { + if( result.param2 == 1 ) + { + player.eventFinish( result.eventId, 1 ); + player.exitInstance(); + } + } ); + } +}; \ No newline at end of file diff --git a/src/servers/sapphire_api/PlayerMinimal.cpp b/src/servers/sapphire_api/PlayerMinimal.cpp index 31e84818..5ca13b9a 100644 --- a/src/servers/sapphire_api/PlayerMinimal.cpp +++ b/src/servers/sapphire_api/PlayerMinimal.cpp @@ -64,7 +64,7 @@ void PlayerMinimal::load( uint32_t charId ) m_guardianDeity = res->getUInt8( "GuardianDeity" ); m_class = res->getUInt8( "Class" ); m_contentId = res->getUInt64( "ContentId" ); - m_zoneId = res->getUInt16( "TerritoryType" ); + m_territoryTypeId = res->getUInt16( "TerritoryType" ); res.reset(); diff --git a/src/servers/sapphire_api/PlayerMinimal.h b/src/servers/sapphire_api/PlayerMinimal.h index 7cbaade8..dda3202e 100644 --- a/src/servers/sapphire_api/PlayerMinimal.h +++ b/src/servers/sapphire_api/PlayerMinimal.h @@ -131,7 +131,7 @@ namespace Sapphire uint32_t getZoneId() const { - return m_zoneId; + return m_territoryTypeId; } uint32_t getTribe() const @@ -185,7 +185,7 @@ namespace Sapphire uint8_t m_tribe; - uint16_t m_zoneId; + uint16_t m_territoryTypeId; uint64_t m_modelMainWeapon; uint64_t m_modelSubWeapon; diff --git a/src/servers/sapphire_zone/Actor/Actor.h b/src/servers/sapphire_zone/Actor/Actor.h index bacb63e0..5f20797d 100644 --- a/src/servers/sapphire_zone/Actor/Actor.h +++ b/src/servers/sapphire_zone/Actor/Actor.h @@ -30,7 +30,7 @@ namespace Sapphire::Entity /*! Type of the actor */ Common::ObjKind m_objKind; /*! Id of the zone the actor currently is in */ - uint32_t m_zoneId; + uint32_t m_territoryTypeId; /*! Ptr to the ZoneObj the actor belongs to */ ZonePtr m_pCurrentZone; diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index 038a95b1..ead5d2d4 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -128,7 +128,7 @@ uint32_t Sapphire::Entity::Player::getMaxMp() uint16_t Sapphire::Entity::Player::getZoneId() const { - return m_zoneId; + return m_territoryTypeId; } uint32_t Sapphire::Entity::Player::getTerritoryId() const @@ -414,9 +414,9 @@ void Sapphire::Entity::Player::setZone( uint32_t zoneId ) // todo: this will require proper handling, for now just return the player to their previous area m_pos = m_prevPos; m_rot = m_prevRot; - m_zoneId = m_prevZoneId; + m_territoryTypeId = m_prevTerritoryTypeId; - if( !pTeriMgr->movePlayer( m_zoneId, getAsPlayer() ) ) + if( !pTeriMgr->movePlayer( m_territoryTypeId, getAsPlayer() ) ) return; } @@ -442,12 +442,15 @@ bool Sapphire::Entity::Player::setInstance( ZonePtr instance ) auto pTeriMgr = g_fw.get< TerritoryMgr >(); + auto currentZone = getCurrentZone(); + // zoning within the same zone won't cause the prev data to be overwritten - if( instance->getTerritoryTypeId() != m_zoneId ) + if( instance->getTerritoryTypeId() != m_territoryTypeId ) { m_prevPos = m_pos; m_prevRot = m_rot; - m_prevZoneId = m_zoneId; + m_prevTerritoryTypeId = currentZone->getTerritoryTypeId(); + m_prevTerritoryId = getTerritoryId(); } if( !pTeriMgr->movePlayer( instance, getAsPlayer() ) ) @@ -461,12 +464,23 @@ bool Sapphire::Entity::Player::setInstance( ZonePtr instance ) bool Sapphire::Entity::Player::exitInstance() { auto pTeriMgr = g_fw.get< TerritoryMgr >(); - if( !pTeriMgr->movePlayer( m_prevZoneId, getAsPlayer() ) ) - return false; + + // check if housing zone + if( pTeriMgr->isHousingTerritory( m_prevTerritoryTypeId ) ) + { + if( !pTeriMgr->movePlayer( pTeriMgr->getZoneByLandSetId( m_prevTerritoryId ), getAsPlayer() ) ) + return false; + } + else + { + if( !pTeriMgr->movePlayer( m_prevTerritoryTypeId, getAsPlayer() ) ) + return false; + } m_pos = m_prevPos; m_rot = m_prevRot; - m_zoneId = m_prevZoneId; + m_territoryTypeId = m_prevTerritoryTypeId; + m_territoryId = m_prevTerritoryId; sendZonePackets(); @@ -1232,7 +1246,7 @@ void Sapphire::Entity::Player::setLoadingComplete( bool bComplete ) void Sapphire::Entity::Player::performZoning( uint16_t zoneId, const Common::FFXIVARR_POSITION3& pos, float rotation ) { m_pos = pos; - m_zoneId = zoneId; + m_territoryTypeId = zoneId; m_bMarkedForZoning = true; setRot( rotation ); setZone( zoneId ); @@ -1530,12 +1544,12 @@ void Sapphire::Entity::Player::setEorzeaTimeOffset( uint64_t timestamp ) void Sapphire::Entity::Player::setTerritoryTypeId( uint32_t territoryTypeId ) { - m_zoneId = territoryTypeId; + m_territoryTypeId = territoryTypeId; } uint32_t Sapphire::Entity::Player::getTerritoryTypeId() const { - return m_zoneId; + return m_territoryTypeId; } void Sapphire::Entity::Player::sendZonePackets() diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index a80cdc6f..8532e21a 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -945,8 +945,8 @@ namespace Sapphire::Entity InventoryMap m_storageMap; Common::FFXIVARR_POSITION3 m_prevPos; - uint32_t m_prevZoneType; - uint32_t m_prevZoneId; + uint32_t m_prevTerritoryTypeId; + uint32_t m_prevTerritoryId; float m_prevRot; uint8_t m_voice; diff --git a/src/servers/sapphire_zone/Actor/PlayerSql.cpp b/src/servers/sapphire_zone/Actor/PlayerSql.cpp index b19fc331..136ddd40 100644 --- a/src/servers/sapphire_zone/Actor/PlayerSql.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerSql.cpp @@ -52,7 +52,8 @@ bool Sapphire::Entity::Player::load( uint32_t charId, SessionPtr pSession ) auto zoneId = res->getUInt( "TerritoryType" ); m_territoryId = res->getUInt( "TerritoryId" ); - m_prevZoneId = res->getUInt( "OTerritoryType" ); + m_prevTerritoryTypeId = res->getUInt( "OTerritoryType" ); + m_prevTerritoryId = res->getUInt( "OTerritoryId" ); // Position m_pos.x = res->getFloat( "PosX" ); @@ -75,7 +76,7 @@ bool Sapphire::Entity::Player::load( uint32_t charId, SessionPtr pSession ) // if none found, revert to previous zone and position if( !pCurrZone ) { - zoneId = m_prevZoneId; + zoneId = m_prevTerritoryTypeId; m_pos.x = m_prevPos.x; m_pos.y = m_prevPos.y; m_pos.z = m_prevPos.z; @@ -92,7 +93,7 @@ bool Sapphire::Entity::Player::load( uint32_t charId, SessionPtr pSession ) pCurrZone = pTeriMgr->getZoneByTerritoryTypeId( zoneId ); } - m_zoneId = zoneId; + m_territoryTypeId = zoneId; // TODO: logic for instances needs to be added here // see if a valid zone could be found for the character @@ -370,15 +371,15 @@ void Sapphire::Entity::Player::updateSql() stmt->setInt( 16, static_cast< uint32_t >( m_bNewGame ) ); stmt->setInt( 17, static_cast< uint32_t >( m_bNewAdventurer ) ); - stmt->setInt( 18, m_zoneId ); // TerritoryType + stmt->setInt( 18, m_territoryTypeId ); // TerritoryType stmt->setInt( 19, m_territoryId ); // TerritoryId stmt->setDouble( 20, m_pos.x ); stmt->setDouble( 21, m_pos.y ); stmt->setDouble( 22, m_pos.z ); stmt->setDouble( 23, getRot() ); - stmt->setInt( 24, m_prevZoneId ); // OTerritoryType - stmt->setInt( 25, m_prevZoneType ); // OTerritoryId + stmt->setInt( 24, m_prevTerritoryTypeId ); // OTerritoryType + stmt->setInt( 25, m_prevTerritoryId ); // OTerritoryId stmt->setDouble( 26, m_prevPos.x ); stmt->setDouble( 27, m_prevPos.y ); stmt->setDouble( 28, m_prevPos.z ); diff --git a/src/servers/sapphire_zone/Manager/TerritoryMgr.cpp b/src/servers/sapphire_zone/Manager/TerritoryMgr.cpp index 9ac81f7e..7859f7cf 100644 --- a/src/servers/sapphire_zone/Manager/TerritoryMgr.cpp +++ b/src/servers/sapphire_zone/Manager/TerritoryMgr.cpp @@ -342,7 +342,7 @@ Sapphire::ZonePtr Sapphire::World::Manager::TerritoryMgr::findOrCreateHousingInt if( !terriInfo ) return nullptr; - auto zone = World::Territory::Housing::make_HousingInteriorTerritory( ident, territoryTypeId, getNextInstanceId(), + auto zone = World::Territory::Housing::make_HousingInteriorTerritory( landIdent, territoryTypeId, getNextInstanceId(), terriInfo->name, house->getHouseName() ); zone->init(); @@ -446,6 +446,29 @@ void Sapphire::World::Manager::TerritoryMgr::updateTerritoryInstances( uint32_t { zone->update( currentTime ); } + + auto pLog = g_fw.get< Logger >(); + + // remove internal house zones with nobody in them + for( auto it = m_landIdentToZonePtrMap.begin(); it != m_landIdentToZonePtrMap.end(); ) + { + auto zone = std::dynamic_pointer_cast< Territory::Housing::HousingInteriorTerritory >( it->second ); + assert( zone ); // wtf?? + + auto diff = std::difftime( currentTime, zone->getLastActivityTime() ); + + // todo: make this timeout configurable, though should be pretty relaxed in any case + if( diff > 60 ) + { + pLog->info( "Removing HousingInteriorTerritory#" + std::to_string( zone->getGuId() ) + " - has been inactive for 60 seconds" ); + + // remove zone from maps + m_zoneSet.erase( zone ); + it = m_landIdentToZonePtrMap.erase( it ); + } + else + it++; + } } Sapphire::World::Manager::TerritoryMgr::InstanceIdList Sapphire::World::Manager::TerritoryMgr::getInstanceContentIdList( uint16_t instanceContentId ) const diff --git a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp index c1160246..277ac909 100644 --- a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp @@ -411,7 +411,13 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX } case ClientTriggerType::RequestHousingItemUI: { - uint8_t ward = ( param12 & 0xFF00 ) >> 8; + // close ui + if( param11 == 1 ) + break; + + // param12 is 0 when inside a house + + uint8_t ward = ( param12 >> 16 ) & 0xFF; uint8_t plot = ( param12 & 0xFF ); auto pShowHousingItemUIPacket = makeActorControl142( player.getId(), ShowHousingItemUI, 0, plot ); diff --git a/src/servers/sapphire_zone/Territory/House.cpp b/src/servers/sapphire_zone/Territory/House.cpp index 5698635c..d5b20ef2 100644 --- a/src/servers/sapphire_zone/Territory/House.cpp +++ b/src/servers/sapphire_zone/Territory/House.cpp @@ -48,11 +48,18 @@ Sapphire::House::House( uint32_t houseId, uint32_t landSetId, uint8_t landId, ui auto housePartColours = res->getBlobVector( "HousePartColours" ); auto models = reinterpret_cast< uint32_t* >( &housePartModels[ 0 ] ); - for( auto i = 0; i < 8; i++ ) { m_houseParts[ i ] = { models[ i ], housePartColours[ i ] }; } + + auto houseInteriorModels = res->getBlobVector( "HouseInteriorModels" ); + + auto interiorModels = reinterpret_cast< uint32_t* >( &houseInteriorModels[ 0 ] ); + for( auto i = 0; i < 10; i++ ) + { + m_houseInteriorParts[ i ] = interiorModels[ i ]; + } } } @@ -67,7 +74,7 @@ void Sapphire::House::updateHouseDb() // BuildTime = 1, Aetheryte = 2, Comment = 3, HouseName = 4, Endorsements = 5, // HousePartModels = 6, HousePartColours = 7, HouseId = 8 auto stmt = pDB->getPreparedStatement( Db::HOUSING_HOUSE_UP ); - stmt->setUInt( 8, m_houseId ); + stmt->setUInt( 9, m_houseId ); stmt->setInt64( 1, m_buildTime ); stmt->setInt( 2, 0 ); @@ -94,6 +101,18 @@ void Sapphire::House::updateHouseDb() stmt->setBinary( 6, tmpModels ); stmt->setBinary( 7, colours ); + models.clear(); + + for( auto i = 0; i < 10; i++ ) + { + models.push_back( m_houseInteriorParts[ i ] ); + } + + std::vector< uint8_t > tmp2Models( models.size() * 4 ); + memcpy( tmp2Models.data(), models.data(), tmp2Models.size() ); + + stmt->setBinary( 8, tmp2Models ); + pDB->execute( stmt ); } @@ -127,6 +146,11 @@ uint8_t Sapphire::House::getHousePartColor( Common::HousePartSlot slot ) const return m_houseParts[ slot ].second; } +uint32_t Sapphire::House::getHouseInteriorPart( Common::HousingInteriorSlot slot ) const +{ + return m_houseInteriorParts[ slot ]; +} + void Sapphire::House::setHousePart( Common::HousePartSlot slot, uint32_t id ) { m_houseParts[ slot ].first = id; @@ -137,6 +161,11 @@ void Sapphire::House::setHousePartColor( Common::HousePartSlot slot, uint32_t id m_houseParts[ slot ].second = id; } +void Sapphire::House::setHouseInteriorPart( Common::HousingInteriorSlot slot, uint32_t id ) +{ + m_houseInteriorParts[ slot ] = id; +} + uint32_t Sapphire::House::getHousePart( Common::HousePartSlot slot ) const { return m_houseParts[ slot ].first; diff --git a/src/servers/sapphire_zone/Territory/House.h b/src/servers/sapphire_zone/Territory/House.h index 6a9196d4..183afd1f 100644 --- a/src/servers/sapphire_zone/Territory/House.h +++ b/src/servers/sapphire_zone/Territory/House.h @@ -34,8 +34,10 @@ namespace Sapphire //functions void setHousePart( Common::HousePartSlot slot, uint32_t id ); void setHousePartColor( Common::HousePartSlot slot, uint32_t id ); + void setHouseInteriorPart( Common::HousingInteriorSlot slot, uint32_t id ); uint32_t getHousePart( Common::HousePartSlot slot ) const; uint8_t getHousePartColor( Common::HousePartSlot slot ) const; + uint32_t getHouseInteriorPart( Common::HousingInteriorSlot slot ) const; HousePartsArray const& getHouseParts() const; @@ -51,6 +53,7 @@ namespace Sapphire uint64_t m_buildTime; HousePartsArray m_houseParts; + uint32_t m_houseInteriorParts[10]; std::string m_estateMessage; std::string m_houseName; diff --git a/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.cpp b/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.cpp index 19974061..373d725a 100644 --- a/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.cpp +++ b/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.cpp @@ -1,17 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Actor/Player.h" +#include "Actor/Actor.h" +#include "Actor/EventObject.h" +#include "Manager/HousingMgr.h" +#include "Territory/Land.h" +#include "Territory/House.h" + +#include "Forwards.h" #include "HousingInteriorTerritory.h" -#include "Common.h" +#include "Framework.h" + +extern Sapphire::Framework g_fw; + +using namespace Sapphire::Common; +using namespace Sapphire::Network::Packets; +using namespace Sapphire::Network::Packets::Server; +using namespace Sapphire::World::Manager; using namespace Sapphire; using namespace Sapphire::World::Territory; -Housing::HousingInteriorTerritory::HousingInteriorTerritory( uint64_t ident, uint16_t territoryTypeId, +Housing::HousingInteriorTerritory::HousingInteriorTerritory( Common::LandIdent ident, uint16_t territoryTypeId, uint32_t guId, const std::string& internalName, const std::string& contentName ) : Zone( territoryTypeId, guId, internalName, contentName ), m_landIdent( ident ) { - + m_lastActivityTime = static_cast< uint32_t >( Util::getTimeSeconds() ); } Housing::HousingInteriorTerritory::~HousingInteriorTerritory() @@ -26,10 +50,56 @@ bool Housing::HousingInteriorTerritory::init() void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player ) { + auto pHousingMgr = g_fw.get< HousingMgr >(); + auto pLog = g_fw.get< Logger >(); + pLog->debug( + "HousingInteriorTerritory::onPlayerZoneIn: Zone#" + std::to_string( getGuId() ) + "|" + std::to_string( getTerritoryTypeId() ) + + ", Entity#" + std::to_string( player.getId() ) ); + + auto indoorInitPacket = makeZonePacket< FFXIVIpcHousingIndoorInitialize >( player.getId() ); + indoorInitPacket->data().u1 = 0; + indoorInitPacket->data().u2 = 0; + indoorInitPacket->data().u3 = 0; + indoorInitPacket->data().u4 = 0; + + auto landSetId = pHousingMgr->toLandSetId( m_landIdent.territoryTypeId, m_landIdent.wardNum ); + auto pLand = pHousingMgr->getHousingZoneByLandSetId( landSetId )->getLand( m_landIdent.landId ); + auto pHouse = pLand->getHouse(); + + for( auto i = 0; i < 10; i++ ) + { + indoorInitPacket->data().indoorItems[ i ] = pHouse->getHouseInteriorPart( (Common::HousingInteriorSlot)i ); + } + + + uint32_t yardPacketNum; + uint32_t yardPacketTotal = 2 + pLand->getSize(); + + player.queuePacket( indoorInitPacket ); + + for( yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) + { + auto objectInitPacket = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() ); + memcpy( &objectInitPacket->data().landIdent, &m_landIdent, sizeof( Common::LandIdent ) ); + objectInitPacket->data().u1 = 0; + objectInitPacket->data().u2 = 100; + objectInitPacket->data().packetNum = yardPacketNum; + objectInitPacket->data().packetTotal = yardPacketTotal; + + //TODO: Add Objects here + + player.queuePacket( objectInitPacket ); + } } void Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime ) { + if( m_playerMap.size() > 0 ) + m_lastActivityTime = currTime; +} +uint32_t Housing::HousingInteriorTerritory::getLastActivityTime() const +{ + return m_lastActivityTime; } \ No newline at end of file diff --git a/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.h b/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.h index 539ebba9..e5be57a9 100644 --- a/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.h +++ b/src/servers/sapphire_zone/Territory/Housing/HousingInteriorTerritory.h @@ -6,7 +6,7 @@ namespace Sapphire::World::Territory::Housing class HousingInteriorTerritory : public Zone { public: - HousingInteriorTerritory( uint64_t ident, uint16_t territoryTypeId, + HousingInteriorTerritory( Common::LandIdent ident, uint16_t territoryTypeId, uint32_t guId, const std::string& internalName, const std::string& contentName ); @@ -18,7 +18,10 @@ namespace Sapphire::World::Territory::Housing void onPlayerZoneIn( Entity::Player& player ) override; void onUpdate( uint32_t currTime ) override; + uint32_t getLastActivityTime() const; + private: - uint64_t m_landIdent; + Common::LandIdent m_landIdent; + uint32_t m_lastActivityTime; }; } \ No newline at end of file diff --git a/src/servers/sapphire_zone/Territory/HousingZone.cpp b/src/servers/sapphire_zone/Territory/HousingZone.cpp index a4f6e49c..b5304093 100644 --- a/src/servers/sapphire_zone/Territory/HousingZone.cpp +++ b/src/servers/sapphire_zone/Territory/HousingZone.cpp @@ -32,7 +32,7 @@ Sapphire::HousingZone::HousingZone( uint8_t wardNum, const std::string& contentName ) : Zone( territoryTypeId, guId, internalName, contentName ), m_wardNum( wardNum ), - m_zoneId( territoryTypeId ), + m_territoryTypeId( territoryTypeId ), m_landSetId( ( static_cast< uint32_t >( territoryTypeId ) << 16 ) | wardNum ) { @@ -49,13 +49,13 @@ bool Sapphire::HousingZone::init() } int housingIndex; - if( m_zoneId == 339 ) + if( m_territoryTypeId == 339 ) housingIndex = 0; - else if( m_zoneId == 340 ) + else if( m_territoryTypeId == 340 ) housingIndex = 1; - else if( m_zoneId == 341 ) + else if( m_territoryTypeId == 341 ) housingIndex = 2; - else if( m_zoneId == 641 ) + else if( m_territoryTypeId == 641 ) housingIndex = 3; auto pExdData = g_fw.get< Data::ExdDataGenerated >(); @@ -96,16 +96,15 @@ void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player ) for( yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) { - auto landsetYardInitializePacket = makeZonePacket< FFXIVIpcLandSetYardInitialize >( player.getId() ); - landsetYardInitializePacket->data().unknown1 = 0xFFFFFFFF; - landsetYardInitializePacket->data().unknown2 = 0xFFFFFFFF; - landsetYardInitializePacket->data().unknown3 = 0xFF; - landsetYardInitializePacket->data().packetNum = yardPacketNum; - landsetYardInitializePacket->data().packetTotal = yardPacketTotal; + auto housingObjectInitializPacket = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() ); + memset( &housingObjectInitializPacket->data().landIdent, 0xFF, sizeof( Common::LandIdent ) ); + housingObjectInitializPacket->data().u1 = 0xFF; + housingObjectInitializPacket->data().packetNum = yardPacketNum; + housingObjectInitializPacket->data().packetTotal = yardPacketTotal; //TODO: Add Objects here - player.queuePacket( landsetYardInitializePacket ); + player.queuePacket( housingObjectInitializPacket ); } auto landSetMap = makeZonePacket< FFXIVIpcLandSetMap >( player.getId() ); diff --git a/src/servers/sapphire_zone/Territory/HousingZone.h b/src/servers/sapphire_zone/Territory/HousingZone.h index 9f0ab003..985bbbba 100644 --- a/src/servers/sapphire_zone/Territory/HousingZone.h +++ b/src/servers/sapphire_zone/Territory/HousingZone.h @@ -56,7 +56,7 @@ namespace Sapphire LandPtrMap m_landPtrMap; uint8_t m_wardNum; uint32_t m_landSetId; - uint32_t m_zoneId; + uint32_t m_territoryTypeId; }; } diff --git a/src/servers/sapphire_zone/Territory/Land.cpp b/src/servers/sapphire_zone/Territory/Land.cpp index 07f6a0b2..d61dcaf5 100644 --- a/src/servers/sapphire_zone/Territory/Land.cpp +++ b/src/servers/sapphire_zone/Territory/Land.cpp @@ -332,10 +332,23 @@ bool Sapphire::Land::setPreset( uint32_t itemId ) m_pHouse = make_House( newId, getLandSetId(), getLandId(), getWardNum(), getTerritoryTypeId() ); } + getHouse()->setHousePart( Common::HousePartSlot::ExteriorRoof, convertItemIdToHousingItemId( housingPreset->exteriorRoof ) ); getHouse()->setHousePart( Common::HousePartSlot::ExteriorWall, convertItemIdToHousingItemId( housingPreset->exteriorWall ) ); getHouse()->setHousePart( Common::HousePartSlot::ExteriorWindow, convertItemIdToHousingItemId( housingPreset->exteriorWindow ) ); getHouse()->setHousePart( Common::HousePartSlot::ExteriorDoor, convertItemIdToHousingItemId( housingPreset->exteriorDoor ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall, convertItemIdToHousingItemId( housingPreset->interiorWall ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor, convertItemIdToHousingItemId( housingPreset->interiorFlooring ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight, convertItemIdToHousingItemId( housingPreset->interiorLighting ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorWall ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorFlooring ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorLighting ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall_Basement, convertItemIdToHousingItemId( housingPreset->basementWall ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor_Basement, convertItemIdToHousingItemId( housingPreset->basementFlooring ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Basement, convertItemIdToHousingItemId( housingPreset->basementLighting ) ); + getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Mansion, convertItemIdToHousingItemId( housingPreset->mansionLighting ) ); + + return true; } diff --git a/src/servers/sapphire_zone/Territory/Zone.cpp b/src/servers/sapphire_zone/Territory/Zone.cpp index 2c37892a..eedbe037 100644 --- a/src/servers/sapphire_zone/Territory/Zone.cpp +++ b/src/servers/sapphire_zone/Territory/Zone.cpp @@ -756,3 +756,8 @@ Sapphire::Entity::EventObjectPtr Sapphire::Zone::registerEObj( const std::string return eObj; } + +Sapphire::Data::TerritoryTypePtr Sapphire::Zone::getTerritoryTypeInfo() const +{ + return m_territoryTypeInfo; +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Territory/Zone.h b/src/servers/sapphire_zone/Territory/Zone.h index 13d27e40..132b797d 100644 --- a/src/servers/sapphire_zone/Territory/Zone.h +++ b/src/servers/sapphire_zone/Territory/Zone.h @@ -16,7 +16,7 @@ #include #include -namespace Sapphire +namespace Sapphire { class Session; @@ -76,6 +76,8 @@ namespace Sapphire void setCurrentFestival( uint16_t festivalId, uint16_t additionalFestivalId = 0 ); + std::shared_ptr< Data::TerritoryType > getTerritoryTypeInfo() const; + virtual bool init(); virtual void loadCellCache();