Housing: Fix fixture edit mode using wrong EditorMode and missing FixtureGuid
The "Edit House Exterior" button sent EditorMode=4 (Customize/interior) instead of EditorMode=6 (ExteriorCustomization). The client checks C_HouseEditor.IsHouseEditorModeActive(ExteriorCustomization) which returned false, so the exterior customization UI never activated. Also the SMSG response had an empty FixtureGuid — the client uses this GUID to determine enter vs exit state for fixture editing. Fixes: - Use HOUSING_EDITOR_MODE_EXTERIOR_CUSTOMIZATION (6) for fixture edit mode - Populate FixtureGuid with the root fixture MeshObject GUID (componentType=9) - Always CREATE Account entity in fixture mode (same pattern as decor edit) - Include all fixture MeshObject CREATEs in same UPDATE_OBJECT packet - Add GetPlotMeshObjects() accessor to HousingMap
This commit is contained in:
@@ -1248,16 +1248,31 @@ void WorldSession::HandleHousingFixtureSetEditMode(WorldPackets::Housing::Housin
|
||||
return;
|
||||
}
|
||||
|
||||
housing->SetEditorMode(housingFixtureSetEditMode.Active ? HOUSING_EDITOR_MODE_CUSTOMIZE : HOUSING_EDITOR_MODE_NONE);
|
||||
// Client enum HouseEditorMode: 4=Customize (interior), 6=ExteriorCustomization (fixture).
|
||||
// The "Edit House Exterior" button sends this opcode ? must use mode 6 so the client's
|
||||
// C_HouseEditor.IsHouseEditorModeActive(Enum.HouseEditorMode.ExteriorCustomization) returns true.
|
||||
housing->SetEditorMode(housingFixtureSetEditMode.Active ? HOUSING_EDITOR_MODE_EXTERIOR_CUSTOMIZATION : HOUSING_EDITOR_MODE_NONE);
|
||||
|
||||
// The response FixtureGuid must be the root fixture MeshObject GUID for the player's plot.
|
||||
// The client compares this GUID against its stored "current exterior root" to determine
|
||||
// enter vs exit state. An empty GUID causes the client to never enter fixture edit mode.
|
||||
ObjectGuid fixtureGuid;
|
||||
if (HousingMap* housingMap = dynamic_cast<HousingMap*>(player->GetMap()))
|
||||
{
|
||||
uint8 plotIndex = housing->GetPlotIndex();
|
||||
auto const& meshMap = housingMap->GetPlotMeshObjects();
|
||||
auto meshItr = meshMap.find(plotIndex);
|
||||
if (meshItr != meshMap.end() && !meshItr->second.empty())
|
||||
fixtureGuid = meshItr->second.front(); // First MeshObject = root fixture (componentType=9)
|
||||
}
|
||||
|
||||
WorldPackets::Housing::HousingFixtureSetEditModeResponse response;
|
||||
response.HouseGuid = housing->GetHouseGuid();
|
||||
response.FixtureGuid = fixtureGuid;
|
||||
response.Result = static_cast<uint8>(HOUSING_RESULT_SUCCESS);
|
||||
SendPacket(response.Write());
|
||||
|
||||
// Sync entity data so the client receives budget values when switching to customize mode.
|
||||
// Without this, the client sees 0/0 budgets because the Player entity's EditorMode changed
|
||||
// but the HousingPlayerHouseEntity/Account entity data was never flushed.
|
||||
// Sync entity data so the client receives budget/storage values when switching to fixture mode.
|
||||
if (housingFixtureSetEditMode.Active)
|
||||
{
|
||||
housing->ResetStoragePopulated();
|
||||
@@ -1272,13 +1287,9 @@ void WorldSession::HandleHousingFixtureSetEditMode(WorldPackets::Housing::Housin
|
||||
WorldPacket updatePacket;
|
||||
player->BuildValuesUpdateBlockForPlayer(&updateData, player);
|
||||
|
||||
if (player->HaveAtClient(&GetBattlenetAccount()))
|
||||
GetBattlenetAccount().BuildValuesUpdateBlockForPlayer(&updateData, player);
|
||||
else
|
||||
{
|
||||
GetBattlenetAccount().BuildCreateUpdateBlockForPlayer(&updateData, player);
|
||||
player->m_clientGUIDs.insert(GetBattlenetAccount().GetGUID());
|
||||
}
|
||||
// Always CREATE Account entity (same reasoning as decor edit mode)
|
||||
GetBattlenetAccount().BuildCreateUpdateBlockForPlayer(&updateData, player);
|
||||
player->m_clientGUIDs.insert(GetBattlenetAccount().GetGUID());
|
||||
|
||||
if (player->HaveAtClient(&GetHousingPlayerHouseEntity()))
|
||||
GetHousingPlayerHouseEntity().BuildValuesUpdateBlockForPlayer(&updateData, player);
|
||||
@@ -1288,6 +1299,25 @@ void WorldSession::HandleHousingFixtureSetEditMode(WorldPackets::Housing::Housin
|
||||
player->m_clientGUIDs.insert(GetHousingPlayerHouseEntity().GetGUID());
|
||||
}
|
||||
|
||||
// Include CREATE for all fixture MeshObjects in same packet (same pattern as decor edit)
|
||||
if (HousingMap* housingMap = dynamic_cast<HousingMap*>(player->GetMap()))
|
||||
{
|
||||
uint8 plotIndex = housing->GetPlotIndex();
|
||||
auto const& meshMap = housingMap->GetPlotMeshObjects();
|
||||
auto meshItr = meshMap.find(plotIndex);
|
||||
if (meshItr != meshMap.end())
|
||||
{
|
||||
for (ObjectGuid const& meshGuid : meshItr->second)
|
||||
{
|
||||
MeshObject* meshObj = housingMap->GetMeshObject(meshGuid);
|
||||
if (!meshObj || !meshObj->IsInWorld())
|
||||
continue;
|
||||
meshObj->BuildCreateUpdateBlockForPlayer(&updateData, player);
|
||||
player->m_clientGUIDs.insert(meshGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateData.BuildPacket(&updatePacket);
|
||||
player->SendDirectMessage(&updatePacket);
|
||||
|
||||
@@ -1296,7 +1326,9 @@ void WorldSession::HandleHousingFixtureSetEditMode(WorldPackets::Housing::Housin
|
||||
GetHousingPlayerHouseEntity().ClearUpdateMask(true);
|
||||
}
|
||||
|
||||
TC_LOG_INFO("housing", "CMSG_HOUSING_FIXTURE_SET_EDITOR_MODE_ACTIVE Active: {}", housingFixtureSetEditMode.Active);
|
||||
TC_LOG_ERROR("housing", "CMSG_HOUSING_FIXTURE_SET_EDITOR_MODE_ACTIVE Active: {} FixtureGuid: {} EditorMode: {}",
|
||||
housingFixtureSetEditMode.Active, fixtureGuid.ToString(),
|
||||
housingFixtureSetEditMode.Active ? 6 : 0);
|
||||
}
|
||||
|
||||
void WorldSession::HandleHousingFixtureSetCoreFixture(WorldPackets::Housing::HousingFixtureSetCoreFixture const& housingFixtureSetCoreFixture)
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
QuaternionData const& houseRot, ObjectGuid houseGuid);
|
||||
void DespawnRoomForPlot(uint8 plotIndex);
|
||||
|
||||
// Decor management (all decor is MeshObject — sniff-verified, never GO)
|
||||
// Decor management (all decor is MeshObject ? sniff-verified, never GO)
|
||||
MeshObject* SpawnDecorItem(uint8 plotIndex, Housing::PlacedDecor const& decor, ObjectGuid houseGuid);
|
||||
void DespawnDecorItem(uint8 plotIndex, ObjectGuid decorGuid);
|
||||
void DespawnAllDecorForPlot(uint8 plotIndex);
|
||||
@@ -108,10 +108,13 @@ public:
|
||||
return itr != _playerCurrentPlot.end() ? static_cast<int8>(itr->second) : -1;
|
||||
}
|
||||
|
||||
// Accessor for diagnostic logging (decor GUID → MeshObject GUID map)
|
||||
// Accessor for diagnostic logging (decor GUID ? MeshObject GUID map)
|
||||
std::unordered_map<ObjectGuid, ObjectGuid> const& GetDecorGuidMap() const { return _decorGuidToGoGuid; }
|
||||
|
||||
// Manual spell packet helpers — called from AddPlayerToMap and at_housing_plot AT script.
|
||||
// Accessor for fixture MeshObjects (plotIndex ? vector of MeshObject GUIDs)
|
||||
std::unordered_map<uint8, std::vector<ObjectGuid>> const& GetPlotMeshObjects() const { return _meshObjects; }
|
||||
|
||||
// Manual spell packet helpers ? called from AddPlayerToMap and at_housing_plot AT script.
|
||||
// These spells don't exist in DB2, so CastSpell() silently fails; manual packets are required.
|
||||
void SendPostTutorialAuras(Player* player);
|
||||
void SendPlotEnterSpellPackets(Player* player, uint8 plotIndex);
|
||||
|
||||
Reference in New Issue
Block a user