Housing: extend Group A mirrors from 1 per plot to 4 per fixture piece
Audit 2026-04-21 (FINDINGS.md sec 1.1) measured 4 Group A Entity mirrors per plot at retail idx 9984; we were spawning one. The previous fix covered only the Type-9 (Base) root, leaving the Roof/Door/Window companions on retail uncreated. _houseMirrorEntities is now a vector<unique_ptr<HousingMirrorEntity>> per plot. SpawnHouseForPlot iterates the same fixture-tier MeshObjects the Group B pass walks (ExteriorComponent Type 9/10/11/12) and pairs a Group A mirror with each. All four share AttachParent = Housing/2 room identity (sniff-decoded "01 c1 XX 12 40 dc" - subType=2, arg2=18) and local pos (0,0,0) - the room is positioned at the plot centre, so the chain resolves there for every piece. Tagging is now an enum (None / Piece / PieceAndRoot) on HousingMirror Entity::InitPositionData. The Type-9 root (pieceIndex 0) keeps both Tag_HouseExteriorPiece and Tag_HouseExteriorRoot - this is the canonical GUID referenced by FHousingPlayerHouse_C.EntityGUID and the world-map icon picker. The other three (Roof/Door/Window) carry only Tag_HouseExteriorPiece. Group B per-mesh mirrors switch to Tagging::None which is identical behaviour to the prior isExteriorRoot=false path. MakeHouseMirrorGuid gains a pieceIndex parameter (default 0) and packs (bnetId<<16)|(plot<<8)|piece so each per-piece GUID is unique while the default-arg call site that proxy emission uses still resolves to the root mirror's GUID. Player::BuildCreateUpdateBlockForPlayer iterates the new GetHouseMirrors() list so all four Group A mirrors land in the initial UPDATE_OBJECT bundle alongside the four Group B ones (8 mirrors per plot total, matching retail idx 9984).
This commit is contained in:
@@ -35,7 +35,7 @@ HousingMirrorEntity::~HousingMirrorEntity() = default;
|
||||
|
||||
void HousingMirrorEntity::InitPositionData(ObjectGuid attachParent,
|
||||
Position const& position, QuaternionData const& rotation,
|
||||
float scale, uint8 attachmentFlags, bool isExteriorRoot)
|
||||
float scale, uint8 attachmentFlags, Tagging tagging)
|
||||
{
|
||||
auto posData = m_values.ModifyValue(&HousingMirrorEntity::m_mirroredPositionData)
|
||||
.ModifyValue(&UF::MirroredPositionData::PositionData);
|
||||
@@ -46,14 +46,22 @@ void HousingMirrorEntity::InitPositionData(ObjectGuid attachParent,
|
||||
SetUpdateFieldValue(posData.ModifyValue(&UF::MirroredMeshObjectData::ScaleLocalSpace), scale);
|
||||
SetUpdateFieldValue(posData.ModifyValue(&UF::MirroredMeshObjectData::AttachmentFlags), attachmentFlags);
|
||||
|
||||
// Retail house-exterior root mirrors carry BOTH tags (Group A at idx 9984
|
||||
// in dump_12.0.1.66838_2026-04-15_09-35-59). Mesh-level mirrors (Group B)
|
||||
// carry neither. We expose isExteriorRoot for the house-exterior case;
|
||||
// callers default it false for mesh-level mirrors.
|
||||
if (isExteriorRoot)
|
||||
// Retail (idx 9984 in dump_12.0.1.66838_2026-04-15_09-35-59) emits 4
|
||||
// Group A mirrors per plot. The Type-9 (Base) root mirror carries both
|
||||
// Tag_HouseExteriorPiece and Tag_HouseExteriorRoot; the other three
|
||||
// (Roof / Door / Window) carry Tag_HouseExteriorPiece only. Group B
|
||||
// per-piece mesh mirrors carry neither tag.
|
||||
switch (tagging)
|
||||
{
|
||||
m_entityFragments.Add(WowCS::EntityFragment::Tag_HouseExteriorPiece, false);
|
||||
m_entityFragments.Add(WowCS::EntityFragment::Tag_HouseExteriorRoot, false);
|
||||
case Tagging::PieceAndRoot:
|
||||
m_entityFragments.Add(WowCS::EntityFragment::Tag_HouseExteriorPiece, false);
|
||||
m_entityFragments.Add(WowCS::EntityFragment::Tag_HouseExteriorRoot, false);
|
||||
break;
|
||||
case Tagging::Piece:
|
||||
m_entityFragments.Add(WowCS::EntityFragment::Tag_HouseExteriorPiece, false);
|
||||
break;
|
||||
case Tagging::None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,19 +41,28 @@ public:
|
||||
HousingMirrorEntity(Map* map, ObjectGuid guid);
|
||||
~HousingMirrorEntity();
|
||||
|
||||
// Tag set this mirror carries — controls which entity-fragment Tag_*
|
||||
// entries are added when InitPositionData is called.
|
||||
// None — Group B per-piece mesh anchor (no tags)
|
||||
// Piece — Group A non-root piece (Tag_HouseExteriorPiece only)
|
||||
// PieceAndRoot — Group A root piece (Tag_HouseExteriorPiece +
|
||||
// Tag_HouseExteriorRoot)
|
||||
enum class Tagging : uint8
|
||||
{
|
||||
None,
|
||||
Piece,
|
||||
PieceAndRoot,
|
||||
};
|
||||
|
||||
// Populate the mirror's position fragment.
|
||||
// attachParent: the housing entity this mirror is attached to
|
||||
// (HousingPlayerHouse for exterior root; MeshObject
|
||||
// for per-piece mirrors)
|
||||
// position/rotation/scale: local-space to attachParent (use (0,0,0)
|
||||
// + identity + 1.0 when the mirror represents the
|
||||
// house root itself)
|
||||
// (room identity for Group A, MeshObject for Group B)
|
||||
// position/rotation/scale: local-space to attachParent
|
||||
// attachmentFlags: usually 3 (sniff-typical)
|
||||
// isExteriorRoot: if true, tags with Tag_HouseExteriorRoot AND
|
||||
// Tag_HouseExteriorPiece to match retail Group A
|
||||
// tagging: which Tag_HouseExterior* fragments to attach (see enum above)
|
||||
void InitPositionData(ObjectGuid attachParent,
|
||||
Position const& position, QuaternionData const& rotation,
|
||||
float scale, uint8 attachmentFlags, bool isExteriorRoot);
|
||||
float scale, uint8 attachmentFlags, Tagging tagging);
|
||||
|
||||
void ClearUpdateMask(bool remove) override;
|
||||
std::string GetNameForLocaleIdx(LocaleConstant locale) const override;
|
||||
|
||||
@@ -3707,8 +3707,11 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c
|
||||
proxy.BuildCreateUpdateBlockForPlayer(data, target);
|
||||
++proxyCount;
|
||||
|
||||
// Bundle the mirror's CREATE into the same UPDATE_OBJECT.
|
||||
if (HousingMirrorEntity* m = hmap->GetHouseMirror(plot.PlotIndex))
|
||||
// Bundle every Group A per-piece mirror's CREATE into the same
|
||||
// UPDATE_OBJECT. Retail emits 4 (one per visible exterior fixture
|
||||
// — Base/Roof/Door/Window). Index 0 is the Type-9 root mirror
|
||||
// referenced by FHousingPlayerHouse_C.EntityGUID.
|
||||
for (HousingMirrorEntity* m : hmap->GetHouseMirrors(plot.PlotIndex))
|
||||
{
|
||||
m->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
++mirrorCount;
|
||||
@@ -3739,7 +3742,7 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c
|
||||
// resolves EntityGUID.
|
||||
if (hmap && ownPlotIndex != INVALID_PLOT_INDEX)
|
||||
{
|
||||
if (HousingMirrorEntity* ownMirror = hmap->GetHouseMirror(ownPlotIndex))
|
||||
for (HousingMirrorEntity* ownMirror : hmap->GetHouseMirrors(ownPlotIndex))
|
||||
ownMirror->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
for (HousingMirrorEntity* ownMeshMirror : hmap->GetHouseMeshMirrors(ownPlotIndex))
|
||||
ownMeshMirror->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
|
||||
@@ -1985,27 +1985,22 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
// Without this, ALL placement attempts fail with OutsidePlotBounds.
|
||||
SpawnRoomForPlot(plotIndex, pos, rot, plotInfo->HouseGuid);
|
||||
|
||||
// Spawn house-exterior root Entity mirror.
|
||||
// Retail pairs each house with a HighGuid::Entity (57, objectType=18)
|
||||
// carrying FMirroredPositionData_C + Tag_HouseExteriorPiece +
|
||||
// Tag_HouseExteriorRoot. Sniff-verified at idx 9984 of
|
||||
// dump_12.0.1.66838_2026-04-15_09-35-59 (4 Group A mirrors).
|
||||
// Group A house-exterior Entity mirrors — one per visible exterior
|
||||
// fixture MeshObject (Base/Roof/Door/Window — ExteriorComponent Type
|
||||
// 9/10/11/12). All four share AttachParent = Housing/2 room identity
|
||||
// (sniff idx 9984 in dump_12.0.1.66838_2026-04-15_09-35-59 decoded
|
||||
// every Group A mirror's AttachParent as `01 c1 XX 12 40 dc` —
|
||||
// subType=2 HousingRoom, arg2=18 HouseRoomID). The Type-9 root piece
|
||||
// (pieceIndex 0) carries Tag_HouseExteriorPiece + Tag_HouseExteriorRoot
|
||||
// and its GUID is the canonical "house mirror GUID" referenced by
|
||||
// FHousingPlayerHouse_C.EntityGUID; the others carry Piece only.
|
||||
//
|
||||
// CRITICAL: AttachParentGUID must be the ROOM entity, NOT the
|
||||
// HousingPlayerHouse. Audit 2026-04-22 decoded retail Group A mirrors'
|
||||
// AttachParent as `01 c1 XX 12 40 dc` — subType=2 (HousingRoom),
|
||||
// arg2=18 (HouseRoomID). The HousingPlayerHouse has no position;
|
||||
// attaching to it left the client unable to compute a world position
|
||||
// for the mirror, so the world-map icon picker had nowhere to place
|
||||
// the own-plot icon. The room entity carries the plot's world
|
||||
// position in its TransportPosition, so chaining via AttachParent
|
||||
// resolves to real coordinates.
|
||||
//
|
||||
// SpawnRoomForPlot was just called above and registered a room entity
|
||||
// in _roomEntities[plotIndex]. Use that GUID.
|
||||
// The HousingPlayerHouse has no position itself; the room entity
|
||||
// carries the plot's world position so chaining via AttachParent
|
||||
// resolves to real coordinates for the world-map icon picker.
|
||||
// SpawnRoomForPlot was just called above and registered the room
|
||||
// identity in _roomIdentityGuids[plotIndex].
|
||||
{
|
||||
// Prefer Housing/2 identity — retail-matching AttachParent for
|
||||
// Group A Entity mirrors (with exterior tags).
|
||||
ObjectGuid roomParentGuid = GetRoomIdentityGuid(plotIndex);
|
||||
if (roomParentGuid.IsEmpty())
|
||||
{
|
||||
@@ -2014,26 +2009,65 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
"mirror AttachParent falls back to HouseGuid (icon may not render)", plotIndex);
|
||||
}
|
||||
|
||||
uint32 bnetId = static_cast<uint32>(plotInfo->OwnerBnetGuid.GetCounter());
|
||||
ObjectGuid mirrorGuid = MakeHouseMirrorGuid(plotIndex, bnetId);
|
||||
auto mirror = std::make_unique<HousingMirrorEntity>(this, mirrorGuid);
|
||||
// Local pos / rot relative to the room parent. Retail's Group A
|
||||
// mirrors use small offsets (a few yards) and the root one uses
|
||||
// zero pos + identity rot. We use zero pos + identity rot since
|
||||
// our room entity is positioned at the plot centre.
|
||||
Position const localPos(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
std::vector<std::unique_ptr<HousingMirrorEntity>>& mirrors = _houseMirrorEntities[plotIndex];
|
||||
mirrors.clear();
|
||||
uint32 const bnetId = static_cast<uint32>(plotInfo->OwnerBnetGuid.GetCounter());
|
||||
uint8 pieceIndex = 0;
|
||||
QuaternionData identity;
|
||||
identity.x = identity.y = identity.z = 0.0f;
|
||||
identity.w = 1.0f;
|
||||
mirror->InitPositionData(roomParentGuid,
|
||||
localPos, identity, /*scale*/ 1.0f, /*attachmentFlags*/ 3,
|
||||
/*isExteriorRoot*/ true);
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: spawned exterior mirror {} for plot {} "
|
||||
"(attach={} [room], localPos=(0,0,0), plotWorldPos=({:.2f},{:.2f},{:.2f}))",
|
||||
mirrorGuid.ToString(), plotIndex,
|
||||
roomParentGuid.ToString(),
|
||||
pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
|
||||
_houseMirrorEntities[plotIndex] = std::move(mirror);
|
||||
// All Group A mirrors share the room identity as AttachParent and
|
||||
// use (0,0,0) local pos — the room is positioned at the plot
|
||||
// centre, so the chain resolves there for every piece. Without a
|
||||
// fresh sniff parse showing exact non-root offsets, sharing the
|
||||
// root's pos preserves the icon-resolution behaviour we already
|
||||
// have for index 0; the additional pieces are pure registry
|
||||
// entries used by the client for spatial categorisation.
|
||||
Position const localPos(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
auto meshItr = _meshObjects.find(plotIndex);
|
||||
if (meshItr != _meshObjects.end())
|
||||
{
|
||||
for (ObjectGuid const& meshGuid : meshItr->second)
|
||||
{
|
||||
MeshObject* mesh = GetMeshObject(meshGuid);
|
||||
if (!mesh || !mesh->m_housingFixtureData.has_value())
|
||||
continue;
|
||||
UF::HousingFixtureData const& fd = *mesh->m_housingFixtureData;
|
||||
uint8 const compType = uint8(fd.ExteriorComponentType);
|
||||
if (compType < 9 || compType > 12)
|
||||
continue;
|
||||
|
||||
ObjectGuid mirrorGuid = MakeHouseMirrorGuid(plotIndex, bnetId, pieceIndex);
|
||||
auto mirror = std::make_unique<HousingMirrorEntity>(this, mirrorGuid);
|
||||
HousingMirrorEntity::Tagging const tagging = (compType == 9)
|
||||
? HousingMirrorEntity::Tagging::PieceAndRoot
|
||||
: HousingMirrorEntity::Tagging::Piece;
|
||||
mirror->InitPositionData(roomParentGuid,
|
||||
localPos, identity, /*scale*/ 1.0f, /*attachmentFlags*/ 3,
|
||||
tagging);
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: spawned Group A mirror[{}] {} "
|
||||
"for plot {} (attach={} [room], type={}, tag={})",
|
||||
pieceIndex, mirrorGuid.ToString(), plotIndex, roomParentGuid.ToString(),
|
||||
compType, compType == 9 ? "PieceAndRoot" : "Piece");
|
||||
mirrors.push_back(std::move(mirror));
|
||||
++pieceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (mirrors.empty())
|
||||
{
|
||||
TC_LOG_ERROR("housing", "HousingMap::SpawnHouseForPlot: no fixture MeshObjects found for plot {}; "
|
||||
"Group A mirrors skipped — FHousingPlayerHouse_C.EntityGUID will dangle and the "
|
||||
"world-map icon will not resolve", plotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: emitted {} Group A mirrors for plot {} "
|
||||
"(plotWorldPos=({:.2f},{:.2f},{:.2f}))",
|
||||
mirrors.size(), plotIndex,
|
||||
pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
|
||||
}
|
||||
}
|
||||
|
||||
// Group B Entity mirrors — FMirroredPositionData_C only (no tags),
|
||||
@@ -2073,7 +2107,7 @@ GameObject* HousingMap::SpawnHouseForPlot(uint8 plotIndex, Position const* custo
|
||||
auto mirror = std::make_unique<HousingMirrorEntity>(this, mirrorGuid);
|
||||
mirror->InitPositionData(meshGuid,
|
||||
localPos, identity, /*scale*/ 1.0f, /*attachmentFlags*/ 3,
|
||||
/*isExteriorRoot*/ false);
|
||||
HousingMirrorEntity::Tagging::None);
|
||||
TC_LOG_DEBUG("housing", "HousingMap::SpawnHouseForPlot: spawned Group B mirror[{}] {} "
|
||||
"for plot {} (attach={} [mesh type={}])",
|
||||
pieceIndex, mirrorGuid.ToString(), plotIndex, meshGuid.ToString(), compType);
|
||||
@@ -3149,7 +3183,11 @@ void HousingMap::DespawnHouseForPlot(uint8 plotIndex)
|
||||
HousingMirrorEntity* HousingMap::GetHouseMirror(uint8 plotIndex) const
|
||||
{
|
||||
auto itr = _houseMirrorEntities.find(plotIndex);
|
||||
return itr != _houseMirrorEntities.end() ? itr->second.get() : nullptr;
|
||||
if (itr == _houseMirrorEntities.end() || itr->second.empty())
|
||||
return nullptr;
|
||||
// Returns the Type-9 (Base) root mirror — the canonical mirror referenced
|
||||
// by FHousingPlayerHouse_C.EntityGUID. Per-piece access via GetHouseMirrors.
|
||||
return itr->second.front().get();
|
||||
}
|
||||
|
||||
ObjectGuid HousingMap::GetHouseMirrorGuid(uint8 plotIndex) const
|
||||
@@ -3159,17 +3197,33 @@ ObjectGuid HousingMap::GetHouseMirrorGuid(uint8 plotIndex) const
|
||||
return ObjectGuid::Empty;
|
||||
}
|
||||
|
||||
ObjectGuid HousingMap::MakeHouseMirrorGuid(uint8 plotIndex, uint32 bnetAccountId) const
|
||||
std::vector<HousingMirrorEntity*> HousingMap::GetHouseMirrors(uint8 plotIndex) const
|
||||
{
|
||||
std::vector<HousingMirrorEntity*> result;
|
||||
auto itr = _houseMirrorEntities.find(plotIndex);
|
||||
if (itr == _houseMirrorEntities.end())
|
||||
return result;
|
||||
result.reserve(itr->second.size());
|
||||
for (auto const& mirror : itr->second)
|
||||
result.push_back(mirror.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
ObjectGuid HousingMap::MakeHouseMirrorGuid(uint8 plotIndex, uint32 bnetAccountId, uint8 pieceIndex /*= 0*/) const
|
||||
{
|
||||
// Deterministic convention: HighGuid::Entity, mapId=GetId() (neighborhood
|
||||
// world map), entry=37361 (synthetic entry for housing mirrors — picked
|
||||
// outside the range of creature/gameobject entries in use), counter packs
|
||||
// plotIndex into the low byte and bnetAccountId into the upper bits so it
|
||||
// stays unique per (plot, owner). realmId/serverId from the current realm
|
||||
// and map context. Same derivation can be used by proxy emission without
|
||||
// needing the mirror object to exist.
|
||||
// outside the range of creature/gameobject entries in use). Counter packs
|
||||
// (bnetAccountId, plotIndex, pieceIndex) so each per-piece Group A mirror
|
||||
// has a unique GUID and the mapping stays deterministic across runs.
|
||||
// pieceIndex 0 is the Type-9 root mirror (canonical "house mirror GUID"
|
||||
// referenced by FHousingPlayerHouse_C.EntityGUID); 1+ are Roof/Door/Window
|
||||
// pieces. Same derivation is used by proxy emission for neighbour plots
|
||||
// without needing the mirror object to exist.
|
||||
constexpr uint32 HOUSING_MIRROR_ENTRY = 37361;
|
||||
uint64 counter = (static_cast<uint64>(bnetAccountId) << 8) | static_cast<uint64>(plotIndex);
|
||||
uint64 counter = (static_cast<uint64>(bnetAccountId) << 16)
|
||||
| (static_cast<uint64>(plotIndex) << 8)
|
||||
| static_cast<uint64>(pieceIndex);
|
||||
return ObjectGuid::Create<HighGuid::Entity>(GetId(), HOUSING_MIRROR_ENTRY, counter);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,10 +90,20 @@ public:
|
||||
// docs/HOUSING_ENTITY_MIRROR.md (pending) / memory/housing_entity_mirror_architecture.md.
|
||||
HousingMirrorEntity* GetHouseMirror(uint8 plotIndex) const;
|
||||
ObjectGuid GetHouseMirrorGuid(uint8 plotIndex) const;
|
||||
// Full list of per-piece Group A mirrors for this plot (one per visible
|
||||
// exterior fixture MeshObject, Type 9/10/11/12). Returns empty if no
|
||||
// house spawned. Index 0 is the Type-9 root (PieceAndRoot tags); the
|
||||
// others (Piece tag only) come from Roof/Door/Window pieces in spawn
|
||||
// order. The root mirror's GUID is the canonical "house mirror GUID"
|
||||
// referenced by FHousingPlayerHouse_C.EntityGUID.
|
||||
std::vector<HousingMirrorEntity*> GetHouseMirrors(uint8 plotIndex) const;
|
||||
// Deterministic mirror-GUID derivation that does not require the mirror to
|
||||
// exist yet — used by proxy emission for neighbour plots whose plot index
|
||||
// and bnet owner are known from NeighborhoodMirror data.
|
||||
ObjectGuid MakeHouseMirrorGuid(uint8 plotIndex, uint32 bnetAccountId) const;
|
||||
// and bnet owner are known from NeighborhoodMirror data. pieceIndex defaults
|
||||
// to 0 (the Type-9 root mirror) which is the canonical GUID referenced by
|
||||
// FHousingPlayerHouse_C.EntityGUID; pieceIndex 1+ identify the non-root
|
||||
// Group A mirrors (Roof/Door/Window).
|
||||
ObjectGuid MakeHouseMirrorGuid(uint8 plotIndex, uint32 bnetAccountId, uint8 pieceIndex = 0) const;
|
||||
|
||||
// "Group B" per-piece mesh-level mirrors. Retail pairs EACH visible
|
||||
// exterior fixture MeshObject (Base/Roof/Door/Window — ExteriorComponent
|
||||
@@ -211,10 +221,15 @@ private:
|
||||
// House structure GO tracking (plotIndex -> house GO GUID)
|
||||
std::unordered_map<uint8, ObjectGuid> _houseGameObjects;
|
||||
|
||||
// House-exterior root Entity mirror (HighGuid::Entity, objectType=18)
|
||||
// tracking (plotIndex -> mirror entity). Owned by the map; lifecycle
|
||||
// 1:1 with the exterior root MeshObject.
|
||||
std::unordered_map<uint8, std::unique_ptr<HousingMirrorEntity>> _houseMirrorEntities;
|
||||
// Group A house-exterior Entity mirrors (HighGuid::Entity, objectType=18)
|
||||
// — one per visible exterior fixture MeshObject (Type 9/10/11/12). The
|
||||
// Type-9 (Base) entry at index 0 carries both Tag_HouseExteriorPiece +
|
||||
// Tag_HouseExteriorRoot and is the GUID referenced by every external
|
||||
// proxy (FHousingPlayerHouse_C.EntityGUID). The other three carry
|
||||
// Tag_HouseExteriorPiece only. Co-spawned with the house and despawned
|
||||
// together. Vector since retail emits 4 of these per plot (sniff idx
|
||||
// 9984 in dump_12.0.1.66838_2026-04-15_09-35-59).
|
||||
std::unordered_map<uint8, std::vector<std::unique_ptr<HousingMirrorEntity>>> _houseMirrorEntities;
|
||||
|
||||
// "Group B" per-piece mesh-level Entity mirrors, one per visible exterior
|
||||
// fixture MeshObject (Type 9/10/11/12). Co-spawned with the house and
|
||||
|
||||
Reference in New Issue
Block a user