Housing: Fix neighborhood faction display and plot purchase issues

This commit is contained in:
luis
2026-03-02 20:00:36 -03:00
parent 117fdb7490
commit d564b75509
4 changed files with 83 additions and 67 deletions
+5 -3
View File
@@ -153,9 +153,11 @@ namespace
if (!interiorMap)
return;
NeighborhoodMapData const* nmData = sHousingMgr.GetNeighborhoodMapDataForWorldMap(
interiorMap->GetSourceNeighborhoodMapId());
int32 faction = nmData ? nmData->FactionRestriction : NEIGHBORHOOD_FACTION_ALLIANCE;
// Use player's team for faction theme, matching HouseInteriorMap::AddPlayerToMap pattern.
// NeighborhoodMapData::FactionRestriction is a bitmask (3 = both factions) and doesn't
// map to the enum values expected by GetFactionDefaultThemeID().
int32 faction = (player->GetTeamId() == TEAM_ALLIANCE)
? NEIGHBORHOOD_FACTION_ALLIANCE : NEIGHBORHOOD_FACTION_HORDE;
interiorMap->DespawnAllRoomMeshObjects();
interiorMap->SpawnRoomMeshObjects(housing, faction);
@@ -1379,42 +1379,33 @@ void WorldSession::HandleNeighborhoodOpenCornerstoneUI(WorldPackets::Neighborhoo
Neighborhood::PlotInfo const* plotInfo = neighborhood->GetPlotInfo(plotIdx);
bool isOwned = plotInfo && !plotInfo->OwnerGuid.IsEmpty();
// Build cornerstone UI response ? wire format verified against retail 12.0.1 build 65940
// Build cornerstone UI response ? wire format verified against retail 12.0.1 build 65940.
// Retail sniff analysis (3 packets):
// - Cost is ALWAYS 0 (cost comes from FJamHousingCornerstone_C entity fragment, not packet)
// - PurchaseStatus is ALWAYS 0
// - IsPlotOwned is ALWAYS false (ownership determined by PlotOwnerGuid being non-empty)
// - CornerstoneGuid is ALWAYS empty
// - CanPurchase is ALWAYS false
// - Owned plots: PlotOwnerGuid=player GUID, NeighborhoodGuid=Housing GUID
// - Unclaimed plots: PlotOwnerGuid=empty, NeighborhoodGuid=empty
WorldPackets::Neighborhood::NeighborhoodOpenCornerstoneUIResponse response;
response.PlotIndex = plotIndex;
// NeighborhoodGuid (?Buffer+56): Controls flag +574 via JamCliNeighborhoodName DataCache.
// Client looks up neighborhood name in the cache using this GUID as the key.
response.NeighborhoodGuid = neighborhood->GetGuid();
response.Cost = plotCost;
response.Cost = 0;
response.PurchaseStatus = 0;
response.IsPlotOwned = false;
response.CanPurchase = false;
response.CornerstoneGuid = ObjectGuid::Empty;
response.NeighborhoodName = neighborhood->GetName();
if (isOwned)
{
// Plot is owned ? send owner info so client knows this plot is claimed
// PlotOwnerGuid (?Buffer+40): Controls flag +573. Non-empty triggers owner name lookup.
response.PlotOwnerGuid = plotInfo->OwnerGuid;
response.IsPlotOwned = true;
// PurchaseStatus 73 = PlotReserved, tells client "Plot already reserved"
response.PurchaseStatus = 73;
response.CanPurchase = false;
// CornerstoneGuid: the cornerstone GO GUID for this plot (if on a HousingMap)
if (HousingMap* housingMap = dynamic_cast<HousingMap*>(player->GetMap()))
{
if (GameObject* cornerstoneGo = housingMap->GetPlotGameObject(plotIdx))
response.CornerstoneGuid = cornerstoneGo->GetGUID();
}
response.NeighborhoodGuid = neighborhood->GetGuid();
}
else
{
// Plot is unclaimed ? purchasable
response.PlotOwnerGuid = ObjectGuid::Empty;
response.IsPlotOwned = false;
// PurchaseStatus 0 = Success (no error), allows the purchase UI to render normally
response.PurchaseStatus = 0;
// CanPurchase bit is 0 in ALL retail packets (both owned and unclaimed purchasable)
response.CanPurchase = false;
response.CornerstoneGuid = ObjectGuid::Empty;
response.NeighborhoodGuid = ObjectGuid::Empty;
}
WorldPacket const* pkt = response.Write();
SendPacket(pkt);
+22
View File
@@ -474,12 +474,22 @@ void HousingMap::SetPlotOwnershipState(uint8 plotIndex, bool owned)
// (NOT map-scoped SetWorldStateValue, which pollutes INIT_WORLD_STATES).
uint32 neighborhoodMapId = _neighborhood->GetNeighborhoodMapID();
std::vector<NeighborhoodPlotData const*> plots = sHousingMgr.GetPlotsForMap(neighborhoodMapId);
TC_LOG_ERROR("housing", "SetPlotOwnershipState: Broadcasting WorldState for plot {} "
"(neighborhoodMapId={}, owned={}, numPlots={})",
plotIndex, neighborhoodMapId, owned, plots.size());
for (NeighborhoodPlotData const* plotData : plots)
{
if (plotData->PlotIndex == static_cast<int32>(plotIndex))
{
if (plotData->WorldState != 0)
{
TC_LOG_ERROR("housing", "SetPlotOwnershipState: Found plot {} with WorldState={}, "
"broadcasting to {} players",
plotIndex, plotData->WorldState,
std::distance(GetPlayers().begin(), GetPlayers().end()));
// Send personalized value to each player on the map
for (MapReference const& ref : GetPlayers())
{
@@ -487,9 +497,21 @@ void HousingMap::SetPlotOwnershipState(uint8 plotIndex, bool owned)
{
HousingPlotOwnerType ownerType = GetPlotOwnerTypeForPlayer(mapPlayer, plotIndex);
mapPlayer->SendUpdateWorldState(plotData->WorldState, static_cast<uint32>(ownerType), false);
TC_LOG_ERROR("housing", "SetPlotOwnershipState: Sent WorldState {} = {} ({}) to player {}",
plotData->WorldState, uint32(ownerType),
ownerType == HOUSING_PLOT_OWNER_SELF ? "SELF" :
ownerType == HOUSING_PLOT_OWNER_FRIEND ? "FRIEND" :
ownerType == HOUSING_PLOT_OWNER_STRANGER ? "STRANGER" : "NONE",
mapPlayer->GetGUID().ToString());
}
}
}
else
{
TC_LOG_ERROR("housing", "SetPlotOwnershipState: Plot {} matched but WorldState=0, skipping broadcast",
plotIndex);
}
break;
}
}
+40 -39
View File
@@ -1045,7 +1045,7 @@ enum OpcodeClient : uint32
CMSG_TRANSMOGRIFY_ITEMS = CMSG_REQUEST_SCHEDULED_PVP_INFO + 1,
};
inline constexpr std::size_t NUM_CMSG_OPCODES = 1938;
inline constexpr std::size_t NUM_CMSG_OPCODES = 1939;
inline constexpr std::ptrdiff_t GetOpcodeArrayIndex(OpcodeClient opcode)
{
@@ -1062,17 +1062,17 @@ inline constexpr std::ptrdiff_t GetOpcodeArrayIndex(OpcodeClient opcode)
case 0x31: return idInGroup < 8 ? idInGroup + 169 : -1;
case 0x32: return idInGroup < 9 ? idInGroup + 177 : -1;
case 0x33: return idInGroup < 36 ? idInGroup + 186 : -1;
case 0x35: return idInGroup < 9 ? idInGroup + 222 : -1;
case 0x37: return idInGroup < 8 ? idInGroup + 231 : -1;
case 0x38: return idInGroup < 16 ? idInGroup + 239 : -1;
case 0x39: return idInGroup < 16 ? idInGroup + 255 : -1;
case 0x3A: return idInGroup < 782 ? idInGroup + 271 : -1;
case 0x3B: return idInGroup < 305 ? idInGroup + 1053 : -1;
case 0x3C: return idInGroup < 1 ? idInGroup + 1358 : -1;
case 0x3D: return idInGroup < 10 ? idInGroup + 1359 : -1;
case 0x3E: return idInGroup < 134 ? idInGroup + 1369 : -1;
case 0x40: return idInGroup < 417 ? idInGroup + 1503 : -1;
case 0x41: return idInGroup < 18 ? idInGroup + 1920 : -1;
case 0x35: return idInGroup < 10 ? idInGroup + 222 : -1;
case 0x37: return idInGroup < 8 ? idInGroup + 232 : -1;
case 0x38: return idInGroup < 16 ? idInGroup + 240 : -1;
case 0x39: return idInGroup < 16 ? idInGroup + 256 : -1;
case 0x3A: return idInGroup < 782 ? idInGroup + 272 : -1;
case 0x3B: return idInGroup < 305 ? idInGroup + 1054 : -1;
case 0x3C: return idInGroup < 1 ? idInGroup + 1359 : -1;
case 0x3D: return idInGroup < 10 ? idInGroup + 1360 : -1;
case 0x3E: return idInGroup < 134 ? idInGroup + 1370 : -1;
case 0x40: return idInGroup < 417 ? idInGroup + 1504 : -1;
case 0x41: return idInGroup < 18 ? idInGroup + 1921 : -1;
default: return -1;
}
}
@@ -2475,39 +2475,40 @@ enum OpcodeServer : uint32
SMSG_ARENA_TEAM_STATS = UNKNOWN_OPCODE,
};
inline constexpr std::size_t NUM_SMSG_OPCODES = 1638;
inline constexpr std::size_t NUM_SMSG_OPCODES = 1643;
inline constexpr std::ptrdiff_t GetOpcodeArrayIndex(OpcodeServer opcode)
{
uint32 idInGroup = opcode & 0xFFFF;
switch (opcode >> 16)
{
case 0x42: return idInGroup < 893 ? idInGroup + 0 : -1;
case 0x43: return idInGroup < 5 ? idInGroup + 893 : -1;
case 0x46: return idInGroup < 20 ? idInGroup + 898 : -1;
case 0x47: return idInGroup < 36 ? idInGroup + 918 : -1;
case 0x48: return idInGroup < 49 ? idInGroup + 954 : -1;
case 0x49: return idInGroup < 14 ? idInGroup + 1003 : -1;
case 0x4A: return idInGroup < 12 ? idInGroup + 1017 : -1;
case 0x4C: return idInGroup < 82 ? idInGroup + 1029 : -1;
case 0x4E: return idInGroup < 72 ? idInGroup + 1111 : -1;
case 0x50: return idInGroup < 2 ? idInGroup + 1183 : -1;
case 0x51: return idInGroup < 11 ? idInGroup + 1185 : -1;
case 0x52: return idInGroup < 8 ? idInGroup + 1196 : -1;
case 0x53: return idInGroup < 8 ? idInGroup + 1204 : -1;
case 0x54: return idInGroup < 35 ? idInGroup + 1212 : -1;
case 0x55: return idInGroup < 8 ? idInGroup + 1247 : -1;
case 0x56: return idInGroup < 34 ? idInGroup + 1255 : -1;
case 0x58: return idInGroup < 1 ? idInGroup + 1289 : -1;
case 0x5A: return idInGroup < 122 ? idInGroup + 1290 : -1;
case 0x5B: return idInGroup < 6 ? idInGroup + 1412 : -1;
case 0x5C: return idInGroup < 23 ? idInGroup + 1418 : -1;
case 0x5E: return idInGroup < 8 ? idInGroup + 1441 : -1;
case 0x5F: return idInGroup < 52 ? idInGroup + 1449 : -1;
case 0x60: return idInGroup < 41 ? idInGroup + 1501 : -1;
case 0x62: return idInGroup < 87 ? idInGroup + 1542 : -1;
case 0x63: return idInGroup < 8 ? idInGroup + 1629 : -1;
case 0x65: return idInGroup < 1 ? idInGroup + 1637 : -1;
case 0x2F: return idInGroup < 2 ? idInGroup + 0 : -1;
case 0x42: return idInGroup < 893 ? idInGroup + 2 : -1;
case 0x43: return idInGroup < 5 ? idInGroup + 895 : -1;
case 0x46: return idInGroup < 20 ? idInGroup + 900 : -1;
case 0x47: return idInGroup < 36 ? idInGroup + 920 : -1;
case 0x48: return idInGroup < 49 ? idInGroup + 956 : -1;
case 0x49: return idInGroup < 14 ? idInGroup + 1005 : -1;
case 0x4A: return idInGroup < 12 ? idInGroup + 1019 : -1;
case 0x4C: return idInGroup < 82 ? idInGroup + 1031 : -1;
case 0x4E: return idInGroup < 72 ? idInGroup + 1113 : -1;
case 0x50: return idInGroup < 2 ? idInGroup + 1185 : -1;
case 0x51: return idInGroup < 13 ? idInGroup + 1187 : -1;
case 0x52: return idInGroup < 8 ? idInGroup + 1200 : -1;
case 0x53: return idInGroup < 8 ? idInGroup + 1208 : -1;
case 0x54: return idInGroup < 35 ? idInGroup + 1216 : -1;
case 0x55: return idInGroup < 9 ? idInGroup + 1251 : -1;
case 0x56: return idInGroup < 34 ? idInGroup + 1260 : -1;
case 0x58: return idInGroup < 1 ? idInGroup + 1294 : -1;
case 0x5A: return idInGroup < 122 ? idInGroup + 1295 : -1;
case 0x5B: return idInGroup < 6 ? idInGroup + 1417 : -1;
case 0x5C: return idInGroup < 23 ? idInGroup + 1423 : -1;
case 0x5E: return idInGroup < 8 ? idInGroup + 1446 : -1;
case 0x5F: return idInGroup < 52 ? idInGroup + 1454 : -1;
case 0x60: return idInGroup < 41 ? idInGroup + 1506 : -1;
case 0x62: return idInGroup < 87 ? idInGroup + 1547 : -1;
case 0x63: return idInGroup < 8 ? idInGroup + 1634 : -1;
case 0x65: return idInGroup < 1 ? idInGroup + 1642 : -1;
default: return -1;
}
}