Housing: Read decor budget values from HouseLevelRewardInfo DB2

Budget values (interior/exterior decor, room, fixture) were hardcoded instead
of being read from the HouseLevelRewardInfo DB2. RewardType 38-41 maps to
ExpectedStatType housing budget enums. Wire these DB2 values into HouseLevelData
during LoadHouseLevelRewardInfoData, with hardcoded values as fallback only when
DB2 entries are missing. Log final budget values per level at startup for
verification.
This commit is contained in:
luis
2026-03-10 13:30:37 -03:00
parent 81c3566786
commit 19c91b9f3c
+113 -50
View File
@@ -194,16 +194,12 @@ void HousingMgr::LoadHouseLevelData()
data.ID = entry->ID;
data.Level = entry->Level;
data.QuestID = entry->QuestID;
// Budget values from captured game data; interior scales per level, others are constant
static constexpr int32 InteriorBudgetByLevel[] = { 0, 910, 1155, 1450, 1750, 2050 };
uint32 lvl = static_cast<uint32>(std::max<int32>(entry->Level, 1));
if (lvl <= 5)
data.InteriorDecorPlacementBudget = InteriorBudgetByLevel[lvl];
else
data.InteriorDecorPlacementBudget = 2050 + static_cast<int32>((lvl - 5) * 300); // +300/level extrapolation
data.ExteriorDecorPlacementBudget = 200;
data.RoomPlacementBudget = 19;
data.ExteriorFixtureBudget = 1000;
// Budget values will be populated from HouseLevelRewardInfo DB2 (RewardType 38-41)
// after LoadHouseLevelRewardInfoData(). Initialize to 0 here; fallbacks applied later.
data.InteriorDecorPlacementBudget = 0;
data.ExteriorDecorPlacementBudget = 0;
data.RoomPlacementBudget = 0;
data.ExteriorFixtureBudget = 0;
}
// Fallback defaults if no DB2 data available
@@ -215,14 +211,10 @@ void HousingMgr::LoadHouseLevelData()
data.ID = level;
data.Level = static_cast<int32>(level);
data.QuestID = 0;
static constexpr int32 InteriorBudgetByLevel[] = { 0, 910, 1155, 1450, 1750, 2050 };
if (level <= 5)
data.InteriorDecorPlacementBudget = InteriorBudgetByLevel[level];
else
data.InteriorDecorPlacementBudget = 2050 + static_cast<int32>((level - 5) * 300);
data.ExteriorDecorPlacementBudget = 200;
data.RoomPlacementBudget = 19;
data.ExteriorFixtureBudget = 1000;
data.InteriorDecorPlacementBudget = 0;
data.ExteriorDecorPlacementBudget = 0;
data.RoomPlacementBudget = 0;
data.ExteriorFixtureBudget = 0;
}
}
@@ -575,7 +567,7 @@ int32 HousingMgr::ResolvePlotIndex(ObjectGuid cornerstoneGuid, Neighborhood cons
}
// Only GameObject GUIDs encode a GO entry that can be matched against cornerstone entries.
// Housing/Neighborhood GUIDs (HighGuid 55) don't have a GO entry — callers sometimes
// Housing/Neighborhood GUIDs (HighGuid 55) don't have a GO entry ? callers sometimes
// pass these for diagnostic purposes; silently return -1.
if (cornerstoneGuid.GetHigh() != HighGuid::GameObject)
{
@@ -715,8 +707,8 @@ std::vector<uint32> HousingMgr::GetStarterDecorIds(uint32 teamId) const
// FirstTimeDecorAcquisition sends one packet per UNIQUE decor ID.
// StartingQuantity determines catalog count, NOT notification count.
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_ALLIANCE = 0x1;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_HORDE = 0x2;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_MASK = 0x3;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_HORDE = 0x2;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_MASK = 0x3;
int32 factionBit = (teamId == ALLIANCE) ? HOUSE_DECOR_FLAG_FACTION_ALLIANCE : HOUSE_DECOR_FLAG_FACTION_HORDE;
@@ -738,8 +730,8 @@ std::vector<std::pair<uint32, int32>> HousingMgr::GetStarterDecorWithQuantities(
{
// Returns {DecorID, StartingQuantity} pairs for populating the catalog
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_ALLIANCE = 0x1;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_HORDE = 0x2;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_MASK = 0x3;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_HORDE = 0x2;
static constexpr int32 HOUSE_DECOR_FLAG_FACTION_MASK = 0x3;
int32 factionBit = (teamId == ALLIANCE) ? HOUSE_DECOR_FLAG_FACTION_ALLIANCE : HOUSE_DECOR_FLAG_FACTION_HORDE;
@@ -766,21 +758,21 @@ bool HousingMgr::CanVisitorAccess(Player const* visitor, Player const* owner, ui
return true;
// Select the correct flag group based on access type
uint32 anyoneFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_ANYONE : HOUSE_SETTING_PLOT_ACCESS_ANYONE;
uint32 anyoneFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_ANYONE : HOUSE_SETTING_PLOT_ACCESS_ANYONE;
uint32 neighborsFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_NEIGHBORS : HOUSE_SETTING_PLOT_ACCESS_NEIGHBORS;
uint32 guildFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_GUILD : HOUSE_SETTING_PLOT_ACCESS_GUILD;
uint32 friendsFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_FRIENDS : HOUSE_SETTING_PLOT_ACCESS_FRIENDS;
uint32 partyFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_PARTY : HOUSE_SETTING_PLOT_ACCESS_PARTY;
uint32 guildFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_GUILD : HOUSE_SETTING_PLOT_ACCESS_GUILD;
uint32 friendsFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_FRIENDS : HOUSE_SETTING_PLOT_ACCESS_FRIENDS;
uint32 partyFlag = isInterior ? HOUSE_SETTING_HOUSE_ACCESS_PARTY : HOUSE_SETTING_PLOT_ACCESS_PARTY;
// If no flags are set at all, default to open access (sniff behavior: plots are public by default)
uint32 accessMask = isInterior
? (HOUSE_SETTING_HOUSE_ACCESS_ANYONE | HOUSE_SETTING_HOUSE_ACCESS_NEIGHBORS |
HOUSE_SETTING_HOUSE_ACCESS_GUILD | HOUSE_SETTING_HOUSE_ACCESS_FRIENDS | HOUSE_SETTING_HOUSE_ACCESS_PARTY)
HOUSE_SETTING_HOUSE_ACCESS_GUILD | HOUSE_SETTING_HOUSE_ACCESS_FRIENDS | HOUSE_SETTING_HOUSE_ACCESS_PARTY)
: (HOUSE_SETTING_PLOT_ACCESS_ANYONE | HOUSE_SETTING_PLOT_ACCESS_NEIGHBORS |
HOUSE_SETTING_PLOT_ACCESS_GUILD | HOUSE_SETTING_PLOT_ACCESS_FRIENDS | HOUSE_SETTING_PLOT_ACCESS_PARTY);
HOUSE_SETTING_PLOT_ACCESS_GUILD | HOUSE_SETTING_PLOT_ACCESS_FRIENDS | HOUSE_SETTING_PLOT_ACCESS_PARTY);
if ((settingsFlags & accessMask) == 0)
return true; // No restrictions configured — open to all
return true; // No restrictions configured ? open to all
if (settingsFlags & anyoneFlag)
return true;
@@ -876,7 +868,72 @@ void HousingMgr::LoadHouseLevelRewardInfoData()
for (auto const& [id, reward] : _houseLevelRewardInfoStore)
_rewardsByLevel[reward.HouseLevelID].push_back(&reward);
TC_LOG_DEBUG("housing", "HousingMgr::LoadHouseLevelRewardInfoData: Loaded {} HouseLevelRewardInfo entries", uint32(_houseLevelRewardInfoStore.size()));
// Wire budget values from HouseLevelRewardInfo into HouseLevelData.
// RewardType maps to ExpectedStatType: 38=InteriorDecor, 39=ExteriorDecor, 40=Room, 41=Fixture.
// HouseLevelRewardInfo.HouseLevelID references HouseLevelData.ID.
uint32 budgetWired = 0;
for (auto const& [id, reward] : _houseLevelRewardInfoStore)
{
auto lvlIt = _houseLevelDataStore.find(reward.HouseLevelID);
if (lvlIt == _houseLevelDataStore.end())
continue;
HouseLevelData& levelData = lvlIt->second;
switch (reward.RewardType)
{
case 38: // HouseInteriorDecorBudget
levelData.InteriorDecorPlacementBudget = reward.RewardValue;
++budgetWired;
break;
case 39: // HouseExteriorDecorBudget
levelData.ExteriorDecorPlacementBudget = reward.RewardValue;
++budgetWired;
break;
case 40: // HouseRoomPlacementBudget
levelData.RoomPlacementBudget = reward.RewardValue;
++budgetWired;
break;
case 41: // HouseFixtureBudget
levelData.ExteriorFixtureBudget = reward.RewardValue;
++budgetWired;
break;
default:
break;
}
}
// Apply fallback budgets for any levels still at 0 (DB2 missing budget rewards)
static constexpr int32 FallbackInteriorByLevel[] = { 0, 910, 1155, 1450, 1750, 2050 };
for (auto& [id, levelData] : _houseLevelDataStore)
{
uint32 lvl = static_cast<uint32>(std::max<int32>(levelData.Level, 1));
if (levelData.InteriorDecorPlacementBudget <= 0)
{
if (lvl <= 5)
levelData.InteriorDecorPlacementBudget = FallbackInteriorByLevel[lvl];
else
levelData.InteriorDecorPlacementBudget = 2050 + static_cast<int32>((lvl - 5) * 300);
}
if (levelData.ExteriorDecorPlacementBudget <= 0)
levelData.ExteriorDecorPlacementBudget = 200;
if (levelData.RoomPlacementBudget <= 0)
levelData.RoomPlacementBudget = 19;
if (levelData.ExteriorFixtureBudget <= 0)
levelData.ExteriorFixtureBudget = 1000;
}
TC_LOG_INFO("housing", "HousingMgr::LoadHouseLevelRewardInfoData: Loaded {} HouseLevelRewardInfo entries, wired {} budget values from DB2",
uint32(_houseLevelRewardInfoStore.size()), budgetWired);
// Log final budget values per level for verification
for (auto const& [id, levelData] : _houseLevelDataStore)
{
TC_LOG_INFO("housing", " Level {} (ID {}): Interior={} Exterior={} Room={} Fixture={}{}",
levelData.Level, id,
levelData.InteriorDecorPlacementBudget, levelData.ExteriorDecorPlacementBudget,
levelData.RoomPlacementBudget, levelData.ExteriorFixtureBudget,
budgetWired > 0 ? " (from DB2)" : " (fallback)");
}
}
void HousingMgr::LoadNeighborhoodInitiativeData()
@@ -959,12 +1016,12 @@ void HousingMgr::LoadRoomComponentData()
{
switch (c.Type)
{
case HOUSING_ROOM_COMPONENT_WALL: ++wallCount; break;
case HOUSING_ROOM_COMPONENT_FLOOR: ++floorCount; break;
case HOUSING_ROOM_COMPONENT_CEILING: ++ceilCount; break;
case HOUSING_ROOM_COMPONENT_DOORWAY:
case HOUSING_ROOM_COMPONENT_DOORWAY_WALL: ++doorwayCount2; break;
default: ++otherCount; break;
case HOUSING_ROOM_COMPONENT_WALL: ++wallCount; break;
case HOUSING_ROOM_COMPONENT_FLOOR: ++floorCount; break;
case HOUSING_ROOM_COMPONENT_CEILING: ++ceilCount; break;
case HOUSING_ROOM_COMPONENT_DOORWAY:
case HOUSING_ROOM_COMPONENT_DOORWAY_WALL: ++doorwayCount2; break;
default: ++otherCount; break;
}
}
}
@@ -1152,7 +1209,7 @@ void HousingMgr::BuildRoomComponentTextureIndex()
_textureByOptionId.clear();
_textureByComponentType.clear();
// Build option→texture link from RoomComponentOptionTexture join table
// Build option?texture link from RoomComponentOptionTexture join table
for (RoomComponentOptionTextureEntry const* link : sRoomComponentOptionTextureStore)
{
if (!link)
@@ -1160,7 +1217,7 @@ void HousingMgr::BuildRoomComponentTextureIndex()
_textureByOptionId[link->RoomComponentOptionID] = link->RoomComponentTextureID;
}
// Build type→texture fallback from RoomComponentTexture
// Build type?texture fallback from RoomComponentTexture
// "Type" in RoomComponentTexture maps to component type (1=wall, 2=floor, 3=ceiling)
for (RoomComponentTextureEntry const* tex : sRoomComponentTextureStore)
{
@@ -1172,7 +1229,7 @@ void HousingMgr::BuildRoomComponentTextureIndex()
}
TC_LOG_INFO("housing", "HousingMgr::BuildRoomComponentTextureIndex: "
"{} option→texture links, {} type→texture fallbacks "
"{} option?texture links, {} type?texture fallbacks "
"(RoomComponentTexture store: {} entries, RoomComponentOptionTexture store: {} entries)",
uint32(_textureByOptionId.size()), uint32(_textureByComponentType.size()),
sRoomComponentTextureStore.GetNumRows(), sRoomComponentOptionTextureStore.GetNumRows());
@@ -1198,7 +1255,7 @@ void HousingMgr::DumpRoomComponentTextureDiagnostics()
{
if (!link)
continue;
TC_LOG_INFO("housing", " OptionTexture [{}] OptionID={} → TextureID={}",
TC_LOG_INFO("housing", " OptionTexture [{}] OptionID={} ? TextureID={}",
link->ID, link->RoomComponentOptionID, link->RoomComponentTextureID);
}
@@ -1241,7 +1298,7 @@ void HousingMgr::BuildExteriorComponentIndexes()
_hooksByExtComp[hook->ExteriorComponentID].push_back(hook);
}
// 2. Build reverse lookup: component's HookID → the component that attaches there
// 2. Build reverse lookup: component's HookID ? the component that attaches there
// Each ExteriorComponent has a HookID field; if > 0, it means "I attach at this hook"
for (ExteriorComponentEntry const* comp : sExteriorComponentStore)
{
@@ -1347,7 +1404,9 @@ void HousingMgr::DumpExteriorComponentDiagnostics()
bool isKnown = false;
for (uint32 compID : knownCompIDs)
if (hook->ExteriorComponentID == compID)
{ isKnown = true; break; }
{
isKnown = true; break;
}
if (isKnown)
{
@@ -1369,7 +1428,9 @@ void HousingMgr::DumpExteriorComponentDiagnostics()
bool isKnown = false;
for (uint32 compID : knownCompIDs)
if (exitPt->ExteriorComponentID == compID)
{ isKnown = true; break; }
{
isKnown = true; break;
}
if (isKnown)
{
@@ -1381,8 +1442,8 @@ void HousingMgr::DumpExteriorComponentDiagnostics()
}
}
// Dump component→hook attachment map (comp.HookID → hook → hook.ExteriorComponentID = parent)
TC_LOG_INFO("housing", " --- Component→Hook attachment chain ---");
// Dump component?hook attachment map (comp.HookID ? hook ? hook.ExteriorComponentID = parent)
TC_LOG_INFO("housing", " --- Component?Hook attachment chain ---");
for (uint32 compID : knownCompIDs)
{
ExteriorComponentEntry const* comp = sExteriorComponentStore.LookupEntry(compID);
@@ -1392,12 +1453,12 @@ void HousingMgr::DumpExteriorComponentDiagnostics()
ExteriorComponentHookEntry const* hook = sExteriorComponentHookStore.LookupEntry(comp->HookID);
if (hook)
{
TC_LOG_INFO("housing", " comp {} (HookID={}) → hook [{}] → parent comp {}",
TC_LOG_INFO("housing", " comp {} (HookID={}) ? hook [{}] ? parent comp {}",
compID, comp->HookID, hook->ID, hook->ExteriorComponentID);
}
else
{
TC_LOG_INFO("housing", " comp {} (HookID={}) → hook NOT FOUND",
TC_LOG_INFO("housing", " comp {} (HookID={}) ? hook NOT FOUND",
compID, comp->HookID);
}
}
@@ -1411,11 +1472,13 @@ void HousingMgr::DumpExteriorComponentDiagnostics()
bool isKnown = false;
for (uint32 compID : knownCompIDs)
if (static_cast<uint32>(xg->ExteriorComponentID) == compID)
{ isKnown = true; break; }
{
isKnown = true; break;
}
if (isKnown)
{
TC_LOG_INFO("housing", " XGroup [{}] comp={} → group={}",
TC_LOG_INFO("housing", " XGroup [{}] comp={} ? group={}",
xg->ID, xg->ExteriorComponentID, xg->ExteriorComponentGroupID);
}
}