diff --git a/src/server/game/Housing/HousingMgr.cpp b/src/server/game/Housing/HousingMgr.cpp index e5cec37ae..9212e1307 100644 --- a/src/server/game/Housing/HousingMgr.cpp +++ b/src/server/game/Housing/HousingMgr.cpp @@ -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(std::max(entry->Level, 1)); - if (lvl <= 5) - data.InteriorDecorPlacementBudget = InteriorBudgetByLevel[lvl]; - else - data.InteriorDecorPlacementBudget = 2050 + static_cast((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(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((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 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> 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(std::max(levelData.Level, 1)); + if (levelData.InteriorDecorPlacementBudget <= 0) + { + if (lvl <= 5) + levelData.InteriorDecorPlacementBudget = FallbackInteriorByLevel[lvl]; + else + levelData.InteriorDecorPlacementBudget = 2050 + static_cast((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(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); } }