Housing: Refactor fixture resolution to direct (type, wmoDataID) lookup
Replace the broken GroupXHook→Group→XGroup chain with a direct _defaultFixtureByTypeWmo index that maps (componentType, wmoDataID) to the default fixture component ID. This correctly resolves fixtures for all 4 racial house styles (Human/NightElf/BloodElf/Orc). Key changes: - BuildExteriorComponentIndexes: filter structural roots by ExteriorComponentType.ParentComponentType==0 with hardcoded fallback for broken DB2 store iteration - New GetDefaultFixtureForType() replaces GetComponentAtHook() - SpawnExtCompTree uses type+WMO lookup for hook children - Door GO spawning fully data-driven from DB2 (entry + position from hook offset + ExitPoint offset) - Root selection: rootOverrides → coreExtCompID → default → first - Fix missing fixture overrides on late-spawn path - Add GetRootComponentOverrides() for player-selected root variants
This commit is contained in:
@@ -1792,12 +1792,18 @@ void WorldSession::HandleHousingFixtureDeleteFixture(WorldPackets::Housing::Hous
|
||||
housingMap->DespawnSingleMeshObject(plotIndex, oldMesh->GetGUID());
|
||||
}
|
||||
|
||||
// Spawn default component back at this hook (DB2 default)
|
||||
ExteriorComponentEntry const* defaultComp = sHousingMgr.GetComponentAtHook(static_cast<int32>(removedHookID), housing->GetCoreExteriorComponentID());
|
||||
if (defaultComp)
|
||||
// Spawn default component back at this hook (DB2 default by type + wmoDataID)
|
||||
ExteriorComponentHookEntry const* hookEntry = sExteriorComponentHookStore.LookupEntry(removedHookID);
|
||||
if (hookEntry)
|
||||
{
|
||||
housingMap->SpawnFixtureAtHook(plotIndex, removedHookID, defaultComp->ID,
|
||||
housing->GetHouseGuid(), static_cast<int32>(housing->GetHouseType()), player);
|
||||
uint32 defaultCompID = sHousingMgr.GetDefaultFixtureForType(
|
||||
static_cast<uint8>(hookEntry->ExteriorComponentTypeID),
|
||||
static_cast<uint32>(housing->GetHouseType()));
|
||||
if (defaultCompID)
|
||||
{
|
||||
housingMap->SpawnFixtureAtHook(plotIndex, removedHookID, defaultCompID,
|
||||
housing->GetHouseGuid(), static_cast<int32>(housing->GetHouseType()), player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1771,51 +1771,38 @@ std::vector<Housing::Fixture const*> Housing::GetFixtures() const
|
||||
|
||||
std::unordered_map<uint32, uint32> Housing::GetFixtureOverrideMap() const
|
||||
{
|
||||
// Build override map from player's fixture selections.
|
||||
// Two kinds of overrides:
|
||||
// 1. Hook-based (CreateFixture): OptionId != 0 ? hookID ? componentID
|
||||
// 2. Root-based (SetCoreFixture non-base): OptionId == 0, Type != 9 (Base)
|
||||
// ? maps defaultRootComponentID ? newComponentID by matching Slot in the group
|
||||
// Build override map from player's hook-based fixture selections.
|
||||
// These are fixtures at hooks (doors, windows, etc.) where OptionId != 0.
|
||||
// Root overrides (base, roof variants) are handled separately via GetRootComponentOverrides().
|
||||
std::unordered_map<uint32, uint32> result;
|
||||
|
||||
uint32 baseCompID = GetCoreExteriorComponentID();
|
||||
int32 groupID = sHousingMgr.GetGroupForComponent(baseCompID);
|
||||
|
||||
for (auto const& [pointId, fixture] : _fixtures)
|
||||
{
|
||||
if (fixture.OptionId != 0)
|
||||
{
|
||||
// Hook-based override (CreateFixture): hookID ? componentID
|
||||
result[fixture.FixturePointId] = fixture.OptionId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Core fixture (SetCoreFixture): OptionId == 0
|
||||
ExteriorComponentEntry const* newComp = sExteriorComponentStore.LookupEntry(fixture.FixturePointId);
|
||||
if (!newComp || newComp->Type == 9) // Base handled by GetCoreExteriorComponentID()
|
||||
continue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Non-base core fixture (roof, door, etc.): find the DEFAULT root component
|
||||
// of the same Slot in the group and override it.
|
||||
if (groupID != 0)
|
||||
{
|
||||
std::vector<uint32> const* groupComps = sHousingMgr.GetComponentsInGroup(groupID);
|
||||
if (groupComps)
|
||||
{
|
||||
for (uint32 defaultCompID : *groupComps)
|
||||
{
|
||||
if (defaultCompID == fixture.FixturePointId)
|
||||
continue;
|
||||
ExteriorComponentEntry const* defaultComp = sExteriorComponentStore.LookupEntry(defaultCompID);
|
||||
if (defaultComp && defaultComp->Type == newComp->Type && defaultComp->ParentComponentID <= 0)
|
||||
{
|
||||
result[defaultCompID] = fixture.FixturePointId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unordered_map<uint8, uint32> Housing::GetRootComponentOverrides() const
|
||||
{
|
||||
// Build override map for player-selected root components per type.
|
||||
// Core fixtures (OptionId == 0) where the component is a root (ParentComponentID == 0)
|
||||
// represent the player's choice for that component type (base, roof, etc.).
|
||||
std::unordered_map<uint8, uint32> result;
|
||||
|
||||
for (auto const& [pointId, fixture] : _fixtures)
|
||||
{
|
||||
if (fixture.OptionId != 0)
|
||||
continue;
|
||||
|
||||
ExteriorComponentEntry const* comp = sExteriorComponentStore.LookupEntry(fixture.FixturePointId);
|
||||
if (!comp || comp->ParentComponentID != 0)
|
||||
continue;
|
||||
if (_houseType != 0 && comp->HouseExteriorWmoDataID != static_cast<uint32>(_houseType))
|
||||
continue;
|
||||
|
||||
result[comp->Type] = fixture.FixturePointId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ public:
|
||||
std::vector<Fixture const*> GetFixtures() const;
|
||||
std::unordered_map<uint32, uint32> GetFixtureOverrideMap() const;
|
||||
uint32 GetCoreExteriorComponentID() const;
|
||||
std::unordered_map<uint8, uint32> GetRootComponentOverrides() const;
|
||||
|
||||
// Catalog operations
|
||||
HousingResult AddToCatalog(uint32 decorEntryId, uint8 sourceType = DECOR_SOURCE_STANDARD, std::string sourceValue = {});
|
||||
|
||||
@@ -385,21 +385,21 @@ void HousingMap::SpawnPlotGameObjects()
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnPlotGameObjects: Plot {} using ExteriorComponentID={}, WmoDataID={}",
|
||||
plotIdx, exteriorComponentID, houseExteriorWmoDataID);
|
||||
|
||||
// Build fixture override map from player's saved fixture selections
|
||||
FixtureOverrideMap fixtureOverrides;
|
||||
if (housing)
|
||||
fixtureOverrides = housing->GetFixtureOverrideMap();
|
||||
// Build fixture + root override maps from player's saved fixture selections
|
||||
FixtureOverrideMap fixtureOverrides = housing->GetFixtureOverrideMap();
|
||||
FixtureOverrideMap const* overridesPtr = fixtureOverrides.empty() ? nullptr : &fixtureOverrides;
|
||||
RootOverrideMap rootOverrides = housing->GetRootComponentOverrides();
|
||||
RootOverrideMap const* rootOvrPtr = rootOverrides.empty() ? nullptr : &rootOverrides;
|
||||
|
||||
GameObject* houseGo = nullptr;
|
||||
if (housing && housing->HasCustomPosition())
|
||||
if (housing->HasCustomPosition())
|
||||
{
|
||||
Position customPos = housing->GetHousePosition();
|
||||
houseGo = SpawnHouseForPlot(plotIdx, &customPos, exteriorComponentID, houseExteriorWmoDataID, overridesPtr);
|
||||
houseGo = SpawnHouseForPlot(plotIdx, &customPos, exteriorComponentID, houseExteriorWmoDataID, overridesPtr, rootOvrPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
houseGo = SpawnHouseForPlot(plotIdx, nullptr, exteriorComponentID, houseExteriorWmoDataID, overridesPtr);
|
||||
houseGo = SpawnHouseForPlot(plotIdx, nullptr, exteriorComponentID, houseExteriorWmoDataID, overridesPtr, rootOvrPtr);
|
||||
}
|
||||
++houseCount;
|
||||
if (houseGo)
|
||||
@@ -676,15 +676,17 @@ bool HousingMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
|
||||
|
||||
auto fixtureOverrides = housing->GetFixtureOverrideMap();
|
||||
FixtureOverrideMap const* overridesPtr = fixtureOverrides.empty() ? nullptr : &fixtureOverrides;
|
||||
auto rootOverrides = housing->GetRootComponentOverrides();
|
||||
RootOverrideMap const* rootOvrPtr = rootOverrides.empty() ? nullptr : &rootOverrides;
|
||||
|
||||
GameObject* go = nullptr;
|
||||
if (housing->HasCustomPosition())
|
||||
{
|
||||
Position customPos = housing->GetHousePosition();
|
||||
go = SpawnHouseForPlot(plotIdx, &customPos, exteriorComponentID, houseExteriorWmoDataID, overridesPtr);
|
||||
go = SpawnHouseForPlot(plotIdx, &customPos, exteriorComponentID, houseExteriorWmoDataID, overridesPtr, rootOvrPtr);
|
||||
}
|
||||
else
|
||||
go = SpawnHouseForPlot(plotIdx, nullptr, exteriorComponentID, houseExteriorWmoDataID, overridesPtr);
|
||||
go = SpawnHouseForPlot(plotIdx, nullptr, exteriorComponentID, houseExteriorWmoDataID, overridesPtr, rootOvrPtr);
|
||||
|
||||
TC_LOG_DEBUG("housing", "HousingMap::AddPlayerToMap: SpawnHouseForPlot result for plot {}: {}",
|
||||
plotIdx, go ? go->GetGUID().ToString() : "FAILED");
|
||||
@@ -713,8 +715,14 @@ bool HousingMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
|
||||
int32 lateExtCompID = static_cast<int32>(housing->GetCoreExteriorComponentID());
|
||||
int32 lateWmoDataID = static_cast<int32>(housing->GetHouseType());
|
||||
|
||||
auto lateFixtureOvr = housing->GetFixtureOverrideMap();
|
||||
FixtureOverrideMap const* lateFixturePtr = lateFixtureOvr.empty() ? nullptr : &lateFixtureOvr;
|
||||
auto lateRootOvr = housing->GetRootComponentOverrides();
|
||||
RootOverrideMap const* lateRootPtr = lateRootOvr.empty() ? nullptr : &lateRootOvr;
|
||||
|
||||
SpawnFullHouseMeshObjects(plotIdx, pos, rot,
|
||||
housing->GetHouseGuid(), lateExtCompID, lateWmoDataID, faction);
|
||||
housing->GetHouseGuid(), lateExtCompID, lateWmoDataID, faction,
|
||||
lateFixturePtr, lateRootPtr);
|
||||
|
||||
// Also spawn room entity + Geobox if not already present
|
||||
if (_roomEntities.find(plotIdx) == _roomEntities.end())
|
||||
@@ -1507,7 +1515,8 @@ void HousingMap::RemovePlayerHousing(ObjectGuid playerGuid)
|
||||
|
||||
GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* customPos,
|
||||
int32 exteriorComponentID, int32 houseExteriorWmoDataID,
|
||||
FixtureOverrideMap const* fixtureOverrides /*= nullptr*/)
|
||||
FixtureOverrideMap const* fixtureOverrides /*= nullptr*/,
|
||||
RootOverrideMap const* rootOverrides /*= nullptr*/)
|
||||
{
|
||||
if (!_neighborhood)
|
||||
return nullptr;
|
||||
@@ -1627,7 +1636,7 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
faction == NEIGHBORHOOD_FACTION_ALLIANCE ? "Alliance" : "Horde");
|
||||
|
||||
SpawnFullHouseMeshObjects(plotIndex, pos, rot, plotInfo->HouseGuid,
|
||||
exteriorComponentID, houseExteriorWmoDataID, faction, fixtureOverrides);
|
||||
exteriorComponentID, houseExteriorWmoDataID, faction, fixtureOverrides, rootOverrides);
|
||||
|
||||
// Spawn room entity + component mesh with Geobox for this plot.
|
||||
// The client uses the MeshObject Geobox to validate decor placement bounds.
|
||||
@@ -1635,42 +1644,95 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
SpawnRoomForPlot(plotIndex, pos, rot, plotInfo->HouseGuid);
|
||||
}
|
||||
|
||||
// Spawn the front door GO (entry 586576, type Goober, displayId 116973)
|
||||
// The interactive door GO must be at the actual doorway (top of stairs), NOT at
|
||||
// the door mesh origin (which sits at stair base level, ~0.56 below house floor).
|
||||
// Door mesh local offset from base: (9.2805, -3.4555, -0.5611) ? mesh origin at stair foot
|
||||
// Adjusted offset: raised Z to door frame level, pulled X back toward actual door threshold.
|
||||
// Spawn the front door interactive GO.
|
||||
// The door's GO entry and position come from DB2:
|
||||
// 1. Resolve the default door component (Type=11) for this house's WMO data
|
||||
// 2. Get the door component's GameObjectID for the GO entry
|
||||
// 3. Find the door hook on the base component for the hook offset
|
||||
// 4. Get the ExitPoint on the door component for the interaction offset
|
||||
// 5. World position = house pos + hook offset + exit point offset
|
||||
GameObject* doorGo = nullptr;
|
||||
uint32 doorEntry = 586576; // retail "Founder's Point Front Door"
|
||||
uint32 doorEntry = 0;
|
||||
float doorLocalX = 0.0f;
|
||||
float doorLocalY = 0.0f;
|
||||
float doorLocalZ = 0.0f;
|
||||
|
||||
// Door interaction GO local-space offset (adjusted from mesh origin to doorway threshold)
|
||||
float doorLocalX = 8.0f;
|
||||
float doorLocalY = -3.4555f;
|
||||
float doorLocalZ = 1.5f;
|
||||
|
||||
// Try to get the offset from DB2 ExteriorComponentExitPoint if available
|
||||
int32 groupID = sHousingMgr.GetGroupForComponent(static_cast<uint32>(exteriorComponentID));
|
||||
if (groupID != 0)
|
||||
// Resolve door component from DB2
|
||||
uint32 doorCompID = 0;
|
||||
if (fixtureOverrides)
|
||||
{
|
||||
std::vector<uint32> const* groupComps = sHousingMgr.GetComponentsInGroup(groupID);
|
||||
if (groupComps)
|
||||
// Check if player has a custom door at a hook ? scan hooks for Type=11
|
||||
auto const* baseHooks = sHousingMgr.GetHooksOnComponent(static_cast<uint32>(exteriorComponentID));
|
||||
if (baseHooks)
|
||||
{
|
||||
for (uint32 compID : *groupComps)
|
||||
for (ExteriorComponentHookEntry const* hook : *baseHooks)
|
||||
{
|
||||
ExteriorComponentExitPointEntry const* exitPt = sHousingMgr.GetExitPoint(compID);
|
||||
if (exitPt)
|
||||
if (hook && hook->ExteriorComponentTypeID == 11)
|
||||
{
|
||||
doorLocalX = exitPt->Position[0];
|
||||
doorLocalY = exitPt->Position[1];
|
||||
doorLocalZ = exitPt->Position[2];
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: Door offset from DB2 "
|
||||
"ExitPoint (comp={}) = ({:.2f}, {:.2f}, {:.2f})",
|
||||
compID, doorLocalX, doorLocalY, doorLocalZ);
|
||||
auto ovrItr = fixtureOverrides->find(hook->ID);
|
||||
if (ovrItr != fixtureOverrides->end())
|
||||
doorCompID = ovrItr->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!doorCompID && houseExteriorWmoDataID > 0)
|
||||
doorCompID = sHousingMgr.GetDefaultFixtureForType(11 /*Door*/, static_cast<uint32>(houseExteriorWmoDataID));
|
||||
|
||||
ExteriorComponentEntry const* doorComp = doorCompID ? sExteriorComponentStore.LookupEntry(doorCompID) : nullptr;
|
||||
if (doorComp)
|
||||
{
|
||||
doorEntry = doorComp->GameObjectID > 0 ? static_cast<uint32>(doorComp->GameObjectID) : 0;
|
||||
|
||||
// Door position: start from the door component's Position (local-space offset from base)
|
||||
doorLocalX = doorComp->Position[0];
|
||||
doorLocalY = doorComp->Position[1];
|
||||
doorLocalZ = doorComp->Position[2];
|
||||
|
||||
// Also look for a hook offset on the base component (Type=11 hook)
|
||||
auto const* baseHooks = sHousingMgr.GetHooksOnComponent(static_cast<uint32>(exteriorComponentID));
|
||||
if (baseHooks)
|
||||
{
|
||||
for (ExteriorComponentHookEntry const* hook : *baseHooks)
|
||||
{
|
||||
if (hook && hook->ExteriorComponentTypeID == 11)
|
||||
{
|
||||
// Hook position provides the attachment point on the base
|
||||
doorLocalX = hook->Position[0];
|
||||
doorLocalY = hook->Position[1];
|
||||
doorLocalZ = hook->Position[2];
|
||||
|
||||
// Add ExitPoint offset (interaction point relative to door)
|
||||
ExteriorComponentExitPointEntry const* exitPt = sHousingMgr.GetExitPoint(doorCompID);
|
||||
if (exitPt)
|
||||
{
|
||||
doorLocalX += exitPt->Position[0];
|
||||
doorLocalY += exitPt->Position[1];
|
||||
doorLocalZ += exitPt->Position[2];
|
||||
}
|
||||
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: Door comp={} entry={} "
|
||||
"hook=({:.2f},{:.2f},{:.2f}) exitPt=({:.2f},{:.2f},{:.2f}) "
|
||||
"final=({:.2f},{:.2f},{:.2f})",
|
||||
doorCompID, doorEntry,
|
||||
hook->Position[0], hook->Position[1], hook->Position[2],
|
||||
exitPt ? exitPt->Position[0] : 0.0f,
|
||||
exitPt ? exitPt->Position[1] : 0.0f,
|
||||
exitPt ? exitPt->Position[2] : 0.0f,
|
||||
doorLocalX, doorLocalY, doorLocalZ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!doorEntry)
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: No door component found "
|
||||
"for plot {} (extComp={}, wmoData={}) ? door GO will not spawn",
|
||||
plotIndex, exteriorComponentID, houseExteriorWmoDataID);
|
||||
}
|
||||
|
||||
// Transform local-space door offset to world space
|
||||
float cosFacing = std::cos(facing);
|
||||
@@ -1680,37 +1742,41 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
float doorZ = z + doorLocalZ;
|
||||
Position doorPos(doorX, doorY, doorZ, facing);
|
||||
|
||||
GameObjectTemplate const* doorTemplate = sObjectMgr->GetGameObjectTemplate(doorEntry);
|
||||
if (doorTemplate)
|
||||
if (doorEntry)
|
||||
{
|
||||
doorGo = GameObject::CreateGameObject(doorEntry, this, doorPos, rot, 255, GO_STATE_READY);
|
||||
if (doorGo)
|
||||
GameObjectTemplate const* doorTemplate = sObjectMgr->GetGameObjectTemplate(doorEntry);
|
||||
if (doorTemplate)
|
||||
{
|
||||
doorGo->SetFlag(GO_FLAG_NODESPAWN);
|
||||
PhasingHandler::InitDbPhaseShift(doorGo->GetPhaseShift(), PHASE_USE_FLAGS_ALWAYS_VISIBLE, 0, 0);
|
||||
|
||||
if (AddToMap(doorGo))
|
||||
doorGo = GameObject::CreateGameObject(doorEntry, this, doorPos, rot, 255, GO_STATE_READY);
|
||||
if (doorGo)
|
||||
{
|
||||
_houseGameObjects[plotIndex] = doorGo->GetGUID();
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: Door GO spawned - entry={} guid={} displayId={} at ({:.1f}, {:.1f}, {:.1f}) (house center: {:.1f}, {:.1f}, {:.1f}) for plot {}",
|
||||
doorEntry, doorGo->GetGUID().ToString(), doorGo->GetDisplayId(), doorX, doorY, doorZ, x, y, z, plotIndex);
|
||||
doorGo->SetFlag(GO_FLAG_NODESPAWN);
|
||||
PhasingHandler::InitDbPhaseShift(doorGo->GetPhaseShift(), PHASE_USE_FLAGS_ALWAYS_VISIBLE, 0, 0);
|
||||
|
||||
if (AddToMap(doorGo))
|
||||
{
|
||||
_houseGameObjects[plotIndex] = doorGo->GetGUID();
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: Door GO spawned - entry={} guid={} "
|
||||
"at ({:.1f}, {:.1f}, {:.1f}) (house center: {:.1f}, {:.1f}, {:.1f}) for plot {}",
|
||||
doorEntry, doorGo->GetGUID().ToString(), doorX, doorY, doorZ, x, y, z, plotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: Door GO AddToMap FAILED for plot {}", plotIndex);
|
||||
delete doorGo;
|
||||
doorGo = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: Door GO AddToMap FAILED for plot {}", plotIndex);
|
||||
delete doorGo;
|
||||
doorGo = nullptr;
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: CreateGameObject FAILED for door entry {} at plot {}", doorEntry, plotIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: CreateGameObject FAILED for door entry {} at plot {}", doorEntry, plotIndex);
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: Door GO template {} NOT FOUND for plot {}", doorEntry, plotIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: Door GO template {} NOT FOUND - door will not spawn for plot {}", doorEntry, plotIndex);
|
||||
}
|
||||
|
||||
return doorGo;
|
||||
}
|
||||
@@ -1986,26 +2052,25 @@ void HousingMap::SpawnFullHouseMeshObjects(uint8 plotIndex, Position const& hous
|
||||
QuaternionData const& houseRot, ObjectGuid houseGuid,
|
||||
int32 exteriorComponentID, int32 houseExteriorWmoDataID,
|
||||
int32 factionRestriction /*= NEIGHBORHOOD_FACTION_ALLIANCE*/,
|
||||
FixtureOverrideMap const* fixtureOverrides /*= nullptr*/)
|
||||
FixtureOverrideMap const* fixtureOverrides /*= nullptr*/,
|
||||
RootOverrideMap const* rootOverrides /*= nullptr*/)
|
||||
{
|
||||
// === DATA-DRIVEN EXTERIOR SPAWNING ===
|
||||
// Try to build the house from DB2 ExteriorComponent tree first.
|
||||
// If the data-driven approach yields 0 meshes, fall back to hardcoded methods.
|
||||
// Build the house from DB2 ExteriorComponent tree.
|
||||
// A house consists of multiple independent root components (Base type=9, Roof type=10, etc.)
|
||||
// all sharing the same HouseExteriorWmoDataID. Each root is spawned independently at the
|
||||
// house position, and each has its own hook children (doors on base, chimney/windows on roof).
|
||||
//
|
||||
// Root selection per type:
|
||||
// 1. Check rootOverrides (player's explicit choice for that type)
|
||||
// 2. Use coreExtCompID for the core type
|
||||
// 3. Fall back to DB2 default (Flags & 0x1 IsDefault)
|
||||
|
||||
uint32 coreExtCompID = static_cast<uint32>(exteriorComponentID);
|
||||
int32 groupID = sHousingMgr.GetGroupForComponent(coreExtCompID);
|
||||
|
||||
ExteriorComponentEntry const* coreComp = sExteriorComponentStore.LookupEntry(coreExtCompID);
|
||||
|
||||
if (coreComp && coreComp->ModelFileDataID > 0 && coreComp->HouseExteriorWmoDataID > 0)
|
||||
{
|
||||
// A house consists of multiple independent root components (Base type=9, Roof type=10, etc.)
|
||||
// all sharing the same HouseExteriorWmoDataID. Each root is spawned independently at the
|
||||
// house position, and each has its own hook children (doors on base, chimney/windows on roof).
|
||||
//
|
||||
// Among roots of the same type, there may be variants (e.g. multiple roof styles). The
|
||||
// coreExtCompID tells us which base variant is selected; for other types we pick the
|
||||
// default (Flags & 0x1 IsDefault) or first available.
|
||||
|
||||
uint32 wmoDataID = coreComp->HouseExteriorWmoDataID;
|
||||
auto const* rootComps = sHousingMgr.GetRootComponentsForWmoData(wmoDataID);
|
||||
uint32 totalSpawned = 0;
|
||||
@@ -2026,29 +2091,30 @@ void HousingMap::SpawnFullHouseMeshObjects(uint8 plotIndex, Position const& hous
|
||||
{
|
||||
uint32 selectedCompID = 0;
|
||||
|
||||
if (type == coreComp->Type)
|
||||
// 1. Check player's root overrides for this type
|
||||
if (rootOverrides)
|
||||
{
|
||||
// For the base type, use the player's selected coreExtCompID
|
||||
auto ovrItr = rootOverrides->find(type);
|
||||
if (ovrItr != rootOverrides->end())
|
||||
selectedCompID = ovrItr->second;
|
||||
}
|
||||
|
||||
// 2. For the core type, use the player's selected coreExtCompID
|
||||
if (!selectedCompID && type == coreComp->Type)
|
||||
selectedCompID = coreExtCompID;
|
||||
}
|
||||
else
|
||||
|
||||
// 3. Fall back to DB2 default for this type + wmoDataID
|
||||
if (!selectedCompID)
|
||||
{
|
||||
// For other types (roof, etc.), pick IsDefault or first available
|
||||
// TODO: check fixture overrides for player-selected roof variant
|
||||
for (uint32 id : compIDs)
|
||||
{
|
||||
ExteriorComponentEntry const* rc = sExteriorComponentStore.LookupEntry(id);
|
||||
if (!rc) continue;
|
||||
if (!selectedCompID)
|
||||
selectedCompID = id; // fallback: first available
|
||||
if (rc->Flags & 0x1)
|
||||
{
|
||||
selectedCompID = id; // prefer IsDefault
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint32 defaultID = sHousingMgr.GetDefaultFixtureForType(type, wmoDataID);
|
||||
if (defaultID)
|
||||
selectedCompID = defaultID;
|
||||
}
|
||||
|
||||
// 4. Last resort: first available in the list
|
||||
if (!selectedCompID && !compIDs.empty())
|
||||
selectedCompID = compIDs[0];
|
||||
|
||||
if (selectedCompID)
|
||||
{
|
||||
ExteriorComponentEntry const* selComp = sExteriorComponentStore.LookupEntry(selectedCompID);
|
||||
@@ -2075,14 +2141,15 @@ void HousingMap::SpawnFullHouseMeshObjects(uint8 plotIndex, Position const& hous
|
||||
return;
|
||||
}
|
||||
|
||||
TC_LOG_WARN("housing", "HousingMap::SpawnFullHouseMeshObjects: Data-driven spawn "
|
||||
"yielded 0 meshes for plot {} wmoDataID {} ? falling back to hardcoded",
|
||||
plotIndex, wmoDataID);
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnFullHouseMeshObjects: Data-driven spawn "
|
||||
"yielded 0 meshes for plot {} wmoDataID {} coreComp {} ? no DB2 data available",
|
||||
plotIndex, wmoDataID, coreExtCompID);
|
||||
return;
|
||||
}
|
||||
else if (!coreComp)
|
||||
{
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnFullHouseMeshObjects: ExteriorComponent {} not found "
|
||||
"? using hardcoded spawn for plot {}", exteriorComponentID, plotIndex);
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnFullHouseMeshObjects: ExteriorComponent {} not found "
|
||||
"? cannot spawn house for plot {}", exteriorComponentID, plotIndex);
|
||||
}
|
||||
|
||||
// === HARDCODED FALLBACK ===
|
||||
@@ -2321,8 +2388,6 @@ uint32 HousingMap::SpawnExtCompTree(uint8 plotIndex, uint32 extCompID,
|
||||
// Use worldPos for children: root's worldPos is itself, child's is the root's position
|
||||
Position const* childWorldPos = worldPos ? worldPos : &pos;
|
||||
|
||||
std::set<uint32> spawnedChildComps; // track to avoid double-spawning
|
||||
|
||||
// Recurse into hooks on this component
|
||||
auto const* hooks = sHousingMgr.GetHooksOnComponent(extCompID);
|
||||
if (hooks)
|
||||
@@ -2332,7 +2397,9 @@ uint32 HousingMap::SpawnExtCompTree(uint8 plotIndex, uint32 extCompID,
|
||||
if (!hook)
|
||||
continue;
|
||||
|
||||
// Find what component attaches at this hook ? check player fixture overrides first
|
||||
// Find what component attaches at this hook:
|
||||
// 1. Check player's fixture overrides (hookID ? componentID)
|
||||
// 2. Fall back to DB2 default: look up by hook's ComponentType + house's WmoDataID
|
||||
ExteriorComponentEntry const* childComp = nullptr;
|
||||
if (fixtureOverrides)
|
||||
{
|
||||
@@ -2341,12 +2408,16 @@ uint32 HousingMap::SpawnExtCompTree(uint8 plotIndex, uint32 extCompID,
|
||||
childComp = sExteriorComponentStore.LookupEntry(overrideItr->second);
|
||||
}
|
||||
if (!childComp)
|
||||
childComp = sHousingMgr.GetComponentAtHook(static_cast<int32>(hook->ID), extCompID);
|
||||
{
|
||||
uint32 defaultCompID = sHousingMgr.GetDefaultFixtureForType(
|
||||
static_cast<uint8>(hook->ExteriorComponentTypeID),
|
||||
static_cast<uint32>(houseExteriorWmoDataID));
|
||||
if (defaultCompID)
|
||||
childComp = sExteriorComponentStore.LookupEntry(defaultCompID);
|
||||
}
|
||||
if (!childComp)
|
||||
continue;
|
||||
|
||||
spawnedChildComps.insert(childComp->ID);
|
||||
|
||||
TC_LOG_DEBUG("housing", "SpawnExtCompTree: parent={} hook={} (type={}) ? child comp {} '{}' (ParentComp={}, ModelFDID={})",
|
||||
extCompID, hook->ID, hook->ExteriorComponentTypeID, childComp->ID,
|
||||
childComp->Name[DEFAULT_LOCALE] ? childComp->Name[DEFAULT_LOCALE] : "",
|
||||
@@ -2376,47 +2447,10 @@ uint32 HousingMap::SpawnExtCompTree(uint8 plotIndex, uint32 extCompID,
|
||||
}
|
||||
}
|
||||
|
||||
// Also walk children linked via ExteriorComponent.ParentComponentID.
|
||||
// These are components (e.g. roofs) where ParentComponentID references this component.
|
||||
// They may not have hooks defining their position ? use the child's Position field as offset.
|
||||
auto const* children = sHousingMgr.GetChildComponents(extCompID);
|
||||
if (children)
|
||||
{
|
||||
for (uint32 childCompID : *children)
|
||||
{
|
||||
if (spawnedChildComps.count(childCompID))
|
||||
continue; // already spawned via hook
|
||||
|
||||
ExteriorComponentEntry const* childComp = sExteriorComponentStore.LookupEntry(childCompID);
|
||||
if (!childComp || childComp->ModelFileDataID <= 0)
|
||||
continue;
|
||||
|
||||
// Only spawn if this is a default component (or if there's no default, take first)
|
||||
// Skip non-default variants ? fixture overrides handle those
|
||||
if (!(childComp->Flags & 0x1))
|
||||
continue;
|
||||
|
||||
TC_LOG_INFO("housing", "SpawnExtCompTree: parent={} ? ParentComponentID child comp {} '{}' "
|
||||
"(type={}, ModelFDID={}, Pos=({:.1f},{:.1f},{:.1f}))",
|
||||
extCompID, childComp->ID,
|
||||
childComp->Name[DEFAULT_LOCALE] ? childComp->Name[DEFAULT_LOCALE] : "",
|
||||
childComp->Type, childComp->ModelFileDataID,
|
||||
childComp->Position[0], childComp->Position[1], childComp->Position[2]);
|
||||
|
||||
// Child's Position field is local-space offset from parent
|
||||
Position childPos(childComp->Position[0], childComp->Position[1], childComp->Position[2], 0.0f);
|
||||
QuaternionData childRot; // identity rotation
|
||||
childRot.x = 0.0f;
|
||||
childRot.y = 0.0f;
|
||||
childRot.z = 0.0f;
|
||||
childRot.w = 1.0f;
|
||||
|
||||
count += SpawnExtCompTree(plotIndex, childCompID,
|
||||
childPos, childRot,
|
||||
houseGuid, houseExteriorWmoDataID,
|
||||
meshGuid, childWorldPos, depth + 1, fixtureOverrides);
|
||||
}
|
||||
}
|
||||
// NOTE: ExteriorComponent.ParentComponentID links are color/dye variants of the same shape,
|
||||
// NOT structural children. They share the same mesh with a different dye (Field_011).
|
||||
// These are NOT spawned as additional meshes ? the player's fixture selection determines
|
||||
// which variant is used, handled via GetRootComponentOverrides() / fixture overrides.
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -60,11 +60,16 @@ public:
|
||||
// When provided, SpawnExtCompTree uses these instead of the DB2 default component at each hook.
|
||||
using FixtureOverrideMap = std::unordered_map<uint32 /*hookID*/, uint32 /*extCompID*/>;
|
||||
|
||||
// Root override map: componentType ? componentID from player's root fixture selections
|
||||
// (e.g., player chose a specific roof variant). When provided, SpawnFullHouseMeshObjects
|
||||
// uses these instead of the DB2 default root for that type.
|
||||
using RootOverrideMap = std::unordered_map<uint8 /*componentType*/, uint32 /*compID*/>;
|
||||
|
||||
// House structure GO management
|
||||
// Sniff-verified defaults: ExteriorComponentID=141 (Stucco Base), HouseExteriorWmoDataID=9 (Human theme)
|
||||
GameObject* SpawnHouseForPlot(uint8 plotIndex, Position const* customPos,
|
||||
int32 exteriorComponentID, int32 houseExteriorWmoDataID,
|
||||
FixtureOverrideMap const* fixtureOverrides = nullptr);
|
||||
FixtureOverrideMap const* fixtureOverrides = nullptr,
|
||||
RootOverrideMap const* rootOverrides = nullptr);
|
||||
void DespawnHouseForPlot(uint8 plotIndex);
|
||||
GameObject* GetHouseGameObject(uint8 plotIndex);
|
||||
int8 GetPlotIndexForHouseGO(ObjectGuid goGuid) const;
|
||||
@@ -83,7 +88,8 @@ public:
|
||||
QuaternionData const& houseRot, ObjectGuid houseGuid,
|
||||
int32 exteriorComponentID, int32 houseExteriorWmoDataID,
|
||||
int32 factionRestriction = NEIGHBORHOOD_FACTION_ALLIANCE,
|
||||
FixtureOverrideMap const* fixtureOverrides = nullptr);
|
||||
FixtureOverrideMap const* fixtureOverrides = nullptr,
|
||||
RootOverrideMap const* rootOverrides = nullptr);
|
||||
void SpawnHordeHouseMeshObjects(uint8 plotIndex, Position const& housePos,
|
||||
QuaternionData const& houseRot, ObjectGuid houseGuid,
|
||||
int32 exteriorComponentID, int32 houseExteriorWmoDataID);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Timer.h"
|
||||
#include "World.h"
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -1261,12 +1262,12 @@ int32 HousingMgr::GetTextureIdForComponentType(uint8 componentType) const
|
||||
void HousingMgr::BuildExteriorComponentIndexes()
|
||||
{
|
||||
_hooksByExtComp.clear();
|
||||
_extCompByHookId.clear();
|
||||
_exitPointByExtComp.clear();
|
||||
_groupByExtComp.clear();
|
||||
_extCompsByGroup.clear();
|
||||
_childrenByExtComp.clear();
|
||||
_rootCompsByWmoDataId.clear();
|
||||
_defaultFixtureByTypeWmo.clear();
|
||||
|
||||
// 1. Build hook index: which hooks are parented to each component
|
||||
// Note: store iteration only yields a subset of entries due to DB2 ParentIndexField
|
||||
@@ -1280,10 +1281,18 @@ void HousingMgr::BuildExteriorComponentIndexes()
|
||||
}
|
||||
|
||||
// 1a. Build child index and root-by-WMO index from ExteriorComponent.
|
||||
// ParentComponentID > 0 ? child of that component.
|
||||
// ParentComponentID == 0 ? root component, indexed by HouseExteriorWmoDataID.
|
||||
// ParentComponentID > 0 ? color/dye variant of that component.
|
||||
// ParentComponentID == 0 ? base variant, and if the Type is a structural root,
|
||||
// it's indexed by HouseExteriorWmoDataID for independent spawning.
|
||||
//
|
||||
// Structural root check: look up ExteriorComponentType by comp->Type.
|
||||
// Only types with ParentComponentType == 0 are structural roots (Base=9, Roof=10).
|
||||
// Types like Door(11), Window(12), Chimney(16) have ParentComponentType > 0
|
||||
// and are spawned as hook children, NOT as independent roots.
|
||||
//
|
||||
// ExteriorComponent uses ParentIndexField (HouseExteriorWmoDataID), so use
|
||||
// LookupEntry over GetNumRows() to reach all entries.
|
||||
std::unordered_set<uint8> structuralRootTypes;
|
||||
for (uint32 i = 0; i < sExteriorComponentStore.GetNumRows(); ++i)
|
||||
{
|
||||
ExteriorComponentEntry const* comp = sExteriorComponentStore.LookupEntry(i);
|
||||
@@ -1293,12 +1302,35 @@ void HousingMgr::BuildExteriorComponentIndexes()
|
||||
if (comp->ParentComponentID > 0)
|
||||
_childrenByExtComp[static_cast<uint32>(comp->ParentComponentID)].push_back(comp->ID);
|
||||
|
||||
// Root components (ParentComponentID=0) with a model, indexed by WMO data ID
|
||||
if (comp->ParentComponentID == 0 && comp->ModelFileDataID > 0 && comp->HouseExteriorWmoDataID > 0)
|
||||
_rootCompsByWmoDataId[comp->HouseExteriorWmoDataID].push_back(comp->ID);
|
||||
}
|
||||
{
|
||||
// Check if this component's Type is a structural root.
|
||||
// ExteriorComponentType DB2: Base(9) and Roof(10) have ParentComponentType=0.
|
||||
// All fixture types (Door=11, Window=12, etc.) have ParentComponentType > 0.
|
||||
// Try DB2 lookup first; fall back to known root types if store is unreliable.
|
||||
bool isStructuralRoot = false;
|
||||
ExteriorComponentTypeEntry const* typeEntry = sExteriorComponentTypeStore.LookupEntry(comp->Type);
|
||||
if (typeEntry)
|
||||
isStructuralRoot = (typeEntry->ParentComponentType == 0);
|
||||
else
|
||||
isStructuralRoot = (comp->Type == 9 || comp->Type == 10); // Base, Roof
|
||||
|
||||
// 1b. Build group indexes from ExteriorComponentXGroup (needed by step 2)
|
||||
if (isStructuralRoot)
|
||||
{
|
||||
_rootCompsByWmoDataId[comp->HouseExteriorWmoDataID].push_back(comp->ID);
|
||||
structuralRootTypes.insert(comp->Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
TC_LOG_DEBUG("housing", "HousingMgr: structural root types: ({})",
|
||||
[&]() {
|
||||
std::string s;
|
||||
for (uint8 t : structuralRootTypes)
|
||||
s += (s.empty() ? "" : ",") + std::to_string(t);
|
||||
return s.empty() ? "none" : s;
|
||||
}());
|
||||
|
||||
// 1c. Build group indexes from ExteriorComponentXGroup (for UI fixture panels)
|
||||
for (ExteriorComponentXGroupEntry const* xg : sExteriorComponentXGroupStore)
|
||||
{
|
||||
if (!xg)
|
||||
@@ -1309,79 +1341,39 @@ void HousingMgr::BuildExteriorComponentIndexes()
|
||||
_extCompsByGroup[groupID].push_back(compID);
|
||||
}
|
||||
|
||||
// 2. Build reverse lookup: hookID ? default component that should be placed there.
|
||||
// Chain: ExteriorComponentGroupXHook maps (GroupID ? HookID), meaning
|
||||
// "this group of components can be installed at this hook."
|
||||
// For each hook, we find which groups link to it, then pick the IsDefault
|
||||
// component from those groups with matching ExteriorComponentTypeID.
|
||||
// 2. Build fixture resolution index: (componentType, wmoDataID) ? default component ID.
|
||||
// For each hook on a parent component, the fixture that goes there is determined by:
|
||||
// - Hook's ExteriorComponentTypeID (e.g., Door=11, Window=12, Chimney=16)
|
||||
// - Parent component's HouseExteriorWmoDataID (e.g., 9=Human, 55=NightElf)
|
||||
// The default fixture is the root component (ParentComponentID==0) with matching
|
||||
// Type and WmoDataID that has Flags & 0x1 (IsDefault).
|
||||
//
|
||||
// NOTE: Hook IDs are NOT globally unique ? the ExteriorComponentHook DB2
|
||||
// uses ExteriorComponentID as a ParentIndexField, so multiple parent
|
||||
// components can share the same hook ID. The _extCompByHookId map uses
|
||||
// a composite key (hookID, parentCompID) to disambiguate.
|
||||
|
||||
// Step 2a: Build reverse index from GroupXHook: hookID ? list of groupIDs
|
||||
std::unordered_map<int32, std::vector<int32>> groupsByHookId; // hookID ? [groupID, ...]
|
||||
for (ExteriorComponentGroupXHookEntry const* gxh : sExteriorComponentGroupXHookStore)
|
||||
// This replaces the old GroupXHook?Group?XGroup chain which was incorrect ?
|
||||
// groups are for UI organization, not fixture resolution.
|
||||
for (uint32 i = 0; i < sExteriorComponentStore.GetNumRows(); ++i)
|
||||
{
|
||||
if (!gxh)
|
||||
continue;
|
||||
groupsByHookId[gxh->ExteriorComponentHookID].push_back(gxh->ExteriorComponentGroupID);
|
||||
}
|
||||
|
||||
// Step 2b: For each hookID in GroupXHook, look up the hook entry via LookupEntry
|
||||
// (store iteration only yields a subset; LookupEntry reaches all 23k+ entries)
|
||||
for (auto const& [hookId, groupList] : groupsByHookId)
|
||||
{
|
||||
ExteriorComponentHookEntry const* hook = sExteriorComponentHookStore.LookupEntry(hookId);
|
||||
if (!hook)
|
||||
ExteriorComponentEntry const* comp = sExteriorComponentStore.LookupEntry(i);
|
||||
if (!comp || comp->ParentComponentID != 0 || comp->HouseExteriorWmoDataID == 0)
|
||||
continue;
|
||||
|
||||
int32 hookType = hook->ExteriorComponentTypeID;
|
||||
int32 parentCompID = hook->ExteriorComponentID;
|
||||
int64 compositeKey = (int64(hookId) << 32) | uint32(parentCompID);
|
||||
ExteriorComponentEntry const* bestComp = nullptr;
|
||||
ExteriorComponentEntry const* fallbackComp = nullptr;
|
||||
uint64 key = (uint64(comp->Type) << 32) | comp->HouseExteriorWmoDataID;
|
||||
bool isDefault = (comp->Flags & 0x1) != 0;
|
||||
|
||||
// Find default component from linked groups
|
||||
for (int32 groupID : groupList)
|
||||
auto existing = _defaultFixtureByTypeWmo.find(key);
|
||||
if (existing == _defaultFixtureByTypeWmo.end())
|
||||
{
|
||||
auto compItr = _extCompsByGroup.find(groupID);
|
||||
if (compItr == _extCompsByGroup.end())
|
||||
continue;
|
||||
|
||||
for (uint32 compID : compItr->second)
|
||||
{
|
||||
ExteriorComponentEntry const* comp = sExteriorComponentStore.LookupEntry(compID);
|
||||
if (!comp || comp->Type != hookType)
|
||||
continue;
|
||||
|
||||
if (!fallbackComp)
|
||||
fallbackComp = comp;
|
||||
|
||||
if (comp->Flags & 0x1) // IsDefaultFixture
|
||||
{
|
||||
bestComp = comp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bestComp)
|
||||
break;
|
||||
// First component for this (type, wmo) ? insert it
|
||||
_defaultFixtureByTypeWmo[key] = comp->ID;
|
||||
}
|
||||
|
||||
if (!bestComp)
|
||||
bestComp = fallbackComp;
|
||||
|
||||
if (bestComp)
|
||||
else if (isDefault)
|
||||
{
|
||||
_extCompByHookId[compositeKey] = bestComp;
|
||||
TC_LOG_DEBUG("housing", "HookID {} (parent={}, type={}) ? comp {} '{}' (Flags=0x{:X}, group-resolved)",
|
||||
hook->ID, parentCompID, hookType, bestComp->ID,
|
||||
bestComp->Name[DEFAULT_LOCALE] ? bestComp->Name[DEFAULT_LOCALE] : "",
|
||||
bestComp->ParentComponentID, bestComp->Flags);
|
||||
// This component is the default ? override any non-default already stored
|
||||
_defaultFixtureByTypeWmo[key] = comp->ID;
|
||||
}
|
||||
}
|
||||
|
||||
TC_LOG_DEBUG("housing", "HousingMgr: Built _defaultFixtureByTypeWmo with {} entries", uint32(_defaultFixtureByTypeWmo.size()));
|
||||
|
||||
// 3. Build exit point index
|
||||
for (ExteriorComponentExitPointEntry const* exitPt : sExteriorComponentExitPointStore)
|
||||
{
|
||||
@@ -1391,8 +1383,8 @@ void HousingMgr::BuildExteriorComponentIndexes()
|
||||
}
|
||||
|
||||
TC_LOG_INFO("housing", "HousingMgr::BuildExteriorComponentIndexes: "
|
||||
"hooks={} compByHook={} exitPoints={} groups={} compsInGroups={} parentChildren={} wmoRoots={}",
|
||||
uint32(_hooksByExtComp.size()), uint32(_extCompByHookId.size()),
|
||||
"hooks={} fixtureByTypeWmo={} exitPoints={} groups={} compsInGroups={} parentChildren={} wmoRoots={}",
|
||||
uint32(_hooksByExtComp.size()), uint32(_defaultFixtureByTypeWmo.size()),
|
||||
uint32(_exitPointByExtComp.size()), uint32(_groupByExtComp.size()),
|
||||
uint32(_extCompsByGroup.size()), uint32(_childrenByExtComp.size()),
|
||||
uint32(_rootCompsByWmoDataId.size()));
|
||||
@@ -1405,11 +1397,11 @@ std::vector<ExteriorComponentHookEntry const*> const* HousingMgr::GetHooksOnComp
|
||||
return itr != _hooksByExtComp.end() ? &itr->second : nullptr;
|
||||
}
|
||||
|
||||
ExteriorComponentEntry const* HousingMgr::GetComponentAtHook(int32 hookID, uint32 parentCompID) const
|
||||
uint32 HousingMgr::GetDefaultFixtureForType(uint8 componentType, uint32 wmoDataID) const
|
||||
{
|
||||
int64 compositeKey = (int64(hookID) << 32) | uint32(parentCompID);
|
||||
auto itr = _extCompByHookId.find(compositeKey);
|
||||
return itr != _extCompByHookId.end() ? itr->second : nullptr;
|
||||
uint64 key = (uint64(componentType) << 32) | wmoDataID;
|
||||
auto itr = _defaultFixtureByTypeWmo.find(key);
|
||||
return itr != _defaultFixtureByTypeWmo.end() ? itr->second : 0;
|
||||
}
|
||||
|
||||
ExteriorComponentExitPointEntry const* HousingMgr::GetExitPoint(uint32 extCompID) const
|
||||
|
||||
@@ -330,13 +330,16 @@ public:
|
||||
|
||||
// ExteriorComponent indexed lookups
|
||||
std::vector<ExteriorComponentHookEntry const*> const* GetHooksOnComponent(uint32 extCompID) const;
|
||||
ExteriorComponentEntry const* GetComponentAtHook(int32 hookID, uint32 parentCompID) const;
|
||||
ExteriorComponentExitPointEntry const* GetExitPoint(uint32 extCompID) const;
|
||||
int32 GetGroupForComponent(uint32 extCompID) const;
|
||||
std::vector<uint32> const* GetChildComponents(uint32 parentCompID) const;
|
||||
std::vector<uint32> const* GetRootComponentsForWmoData(uint32 wmoDataID) const;
|
||||
std::vector<uint32> const* GetComponentsInGroup(int32 groupID) const;
|
||||
|
||||
// Fixture resolution: given a hook's component type and the house's WmoDataID,
|
||||
// returns the default fixture component ID (Flags & 0x1, ParentComponentID == 0).
|
||||
uint32 GetDefaultFixtureForType(uint8 componentType, uint32 wmoDataID) const;
|
||||
|
||||
// Find the first HouseRoom entry with visual components (not the base room 18)
|
||||
uint32 GetDefaultVisualRoomEntry() const;
|
||||
|
||||
@@ -434,12 +437,15 @@ private:
|
||||
|
||||
// ExteriorComponent indexes
|
||||
std::unordered_map<uint32 /*extCompID*/, std::vector<ExteriorComponentHookEntry const*>> _hooksByExtComp;
|
||||
std::unordered_map<int64 /*(hookID<<32)|parentCompID*/, ExteriorComponentEntry const*> _extCompByHookId;
|
||||
std::unordered_map<uint32 /*extCompID*/, ExteriorComponentExitPointEntry const*> _exitPointByExtComp;
|
||||
std::unordered_map<uint32 /*extCompID*/, int32 /*groupID*/> _groupByExtComp;
|
||||
std::unordered_map<int32 /*groupID*/, std::vector<uint32 /*extCompID*/>> _extCompsByGroup;
|
||||
std::unordered_map<uint32 /*parentCompID*/, std::vector<uint32 /*childCompID*/>> _childrenByExtComp;
|
||||
std::unordered_map<uint32 /*wmoDataID*/, std::vector<uint32 /*compID*/>> _rootCompsByWmoDataId;
|
||||
|
||||
// Fixture resolution: (componentType, wmoDataID) ? default component ID
|
||||
// Key = (uint64(componentType) << 32) | wmoDataID
|
||||
std::unordered_map<uint64, uint32> _defaultFixtureByTypeWmo;
|
||||
};
|
||||
|
||||
#define sHousingMgr HousingMgr::Instance()
|
||||
|
||||
Reference in New Issue
Block a user