The AdaptiveAIUpdateThrottler was preventing UpdateStrategies() from
ever being reached, leaving bots idle with active but never-executed
strategies. Also swapped quest/loot priority so quest (MOVEMENT=45)
runs below loot (FOLLOW=50) only when loot is gated by IsActive.
Added temporary STRAT-SELECT diagnostic logging to trace which
strategy wins priority selection each tick.
Rename misnamed SQL columns to match WoWDBDefs 12.0:
- Aura → BattlePetEffectPropertiesID
- BattlePetEffectPropertiesID → AuraBattlePetAbilityID
- VisualID → BattlePetVisualID
Add migration script for existing databases, update SELECT query.
Also add DisplayID=0 repair in LoadPlayerTeam and a log warning
in SelectPetDisplay when creature_template is missing.
Solo strategy activation (rest, solo_combat, quest, grind, loot, solo)
was positioned after both the AI update throttler and the death recovery
guard in UpdateAI(). Bots that were throttled or dead at login never
reached the activation code, leaving them with Strategies=0 and idle.
Move the one-time strategy activation to run before all guards so it
executes on the first UpdateAI tick regardless of throttle/death state.
Fix ExteriorComponentHookEntry struct field order to match DB2 LoadInfo
(Position/Rotation before ID when IndexField=2), resolving garbage hook
IDs during fixture resolution.
Replace first-match door hook selection with center-front scoring
heuristic (|X|*2 + Y) to ensure the main entrance spawns at the front
of the house rather than on a side/back wall.
Add fixture validation in SelectFixtureOption: enforce component type
must match hook type, and only one door allowed per house. This prevents
placing windows at door hooks and spawning multiple entrances.
Only auto-resolve the single best door hook during SpawnExtCompTree —
other fixture types (windows, chimneys, dormers) require explicit player
selection, matching retail behavior where they unlock via progression.
Additional fixes: unique fixture GUIDs via atomic counter (subType=5),
size-aware default fixture lookup, range-based DB2 store iteration,
and starter fixture migration that preserves existing roots.
Map player race to house exterior WMO style on purchase:
Night Elf → Woodland (55), Blood Elf → Engraved (56),
other Alliance → Human (9), other Horde → Orc (87).
On house creation, persist starter fixtures (Base + Roof) to
character_housing_fixtures so spawning reads from DB rather than
relying on runtime default resolution. Door auto-resolves from
the hook system via GetDefaultFixtureForType.
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
- Fix DB2 store iteration bug: ExteriorComponentHook entries with
ParentIndexField were only partially accessible via range-based
iteration (1808 of 23881 entries). Use LookupEntry() over
GetNumRows() to reach all entries, fixing hook resolution
(compByHook was 0, now resolves all 1461 GroupXHook mappings).
- Spawn all root components per HouseExteriorWmoDataID: houses
consist of multiple independent roots (Base type=9, Roof type=10)
sharing the same WMO data ID. Each root spawns independently at
house position with its own hook children (doors on base,
chimney/windows on roof). Filter by Size to avoid spawning
small/medium/large variants simultaneously.
- Build parent-child index from ExteriorComponent.ParentComponentID
for component variants that reference a parent component.
- Add _rootCompsByWmoDataId index for fast lookup of all root
components belonging to a house exterior.
- Fix MeshObject fixture data to pass fixtureGuid and
parentFixtureGuid for proper client-side attachment hierarchy.
- Fix BNetAccount dirty state in fixture edit mode by clearing
update mask after PopulateCatalogStorageEntries.
Comprehensive audit of all neighborhood and housing DB2 tables against
WoWDBDefs canonical field definitions. Renames all misnamed fields across
DB2Structure.h, DB2LoadInfo.h, and internal cache structs in HousingMgr.h.
Key field corrections across 12 DB2 tables:
- NeighborhoodMap: Radius→EntryRotation, PlotCount→UiTextureKitID, FactionRestriction→Flags
- NeighborhoodNameGen: Suffix→Middle, FullName→Suffix
- HouseTheme: IconFileDataID→Flags, CategoryID→ParentThemeID
- HouseDecorMaterial: 5 fields renamed (WMOMaterialReference, MaterialTextureIndex, etc.)
- HouseLevelRewardInfo: HouseLevelID→HouseLevelDataID, RewardType→Field_4, RewardValue→IconFileDataID
- InitiativeCycle: Duration→HouseXPCap
- InitiativeMilestone: 4 fields renamed (MilestoneOrderIndex, RequiredContributionAmount, etc.)
- InitiativeReward: 7 fields renamed (Money, DecorID, DecorQuantity, Favor, RewardQuestID, etc.)
- InitiativeTask: 6 fields renamed (CriteriaTreeID, QuestID, ProgressContributionAmount, etc.)
- DecorCategory/DecorSubcategory/DecorDyeSlot: display fields renamed
Logic bugs revealed and fixed by the audit:
- Budget wiring removed (DB2 has no budget type/value; budgets come from hardcoded table)
- Initiative task type filtering removed (CriteriaTreeID is a FK, not a type enum)
- Initiative target counts fixed (was using QuestID as threshold, now ProgressContributionAmount)
- Initiative duration sourced from NeighborhoodInitiative.Duration, not InitiativeCycle.HouseXPCap
- Initiative rewards rewritten to use correct DB2 fields (Money, DecorID, Favor, etc.)
- Plot count derived from actual plot data instead of misnamed UiTextureKitID field
- NeighborhoodMgr faction checks updated to use Flags with confirmed bitmask values
CriteriaTree-based initiative task matching:
- Replace 4 ad-hoc OnPlayerAction() calls that used non-existent TaskType enum (1-4)
- Add single hook in CriteriaHandler::UpdateCriteria after validation passes
- BuildCriteriaIndex() walks each task's CriteriaTree to find leaf Criteria entries,
builds reverse index CriteriaID → (neighborhood, initiative, task) for O(1) lookup
- OnCriteriaProgress() matches criteria fires against active initiative tasks,
covering all 250+ criteria types (kills, crafting, gathering, quests, etc.) automatically
- Index rebuilt on initiative start/complete to stay current
PlayerPositions in SMSG_PET_BATTLE_FINALIZE_LOCATION does not move the
player character. Use NearTeleportTo to explicitly position the player
5 units behind the midpoint along the facing axis.
- Weather now targets environment slot 2 (PBOID 8 / PetbattleEnviros::Weather)
instead of slot 0 (Pad0), matching client expectations
- Replace hardcoded weather ability name list with generic DB2-driven detection:
PropsID chain walk + reverse AuraBattlePetAbilityID walk + BattlePetAbilityState
diagnostic logging to discover remaining weather abilities
- Multi-hit abilities now stop when the target dies (prevents overkill)
- Battle positioning: offset player 5 units behind pet along facing axis
The BattlePetEffectPropertiesID values in the DB2 don't map 0-18
sequentially — they're arbitrary IDs (222, 26, 24, etc.). Add startup
logging that dumps all unique PropertiesIDs with their ParamLabel
strings and usage counts, so we can build the correct mapping.
Also improve the UNHANDLED effect warning to include all 6 Param values
and the ParamLabel strings from the DB2, making the logs self-documenting.
Two bugs prevented TryMarkAsWildBattlePet() from ever marking critters:
1. Init order: BattlePetMgr::Initialize() ran AFTER sMapMgr->Initialize(),
so the species-by-creature map was empty during creature spawning.
Moved battle pet initialization before map system startup.
2. Respawn flag wipe: TryMarkAsWildBattlePet() ran BEFORE
setDeathState(JUST_RESPAWNED), which calls ReplaceAllNpcFlags from
template, wiping the dynamically added UNIT_NPC_FLAG_WILD_BATTLE_PET.
Moved TryMarkAsWildBattlePet() to after setDeathState.
- Move TickWeather() inside AURA_PROCESSING_BEGIN/END block so client
processes weather effects correctly (was outside the wrapper)
- Emit AURA_CHANGE for environment auras each round with CurrentRound
increment (was missing — pet auras had this but weather did not)
- Add AURA_APPLY/AURA_CANCEL for multi-turn abilities so client shows
buff icon during multi-turn sequences (Burrow, Lift-Off, etc.)
- Fix HandlePetBattleInput to only accept input during ROUND_IN_PROGRESS
state, preventing ProcessRound from firing with dead front pet during
WAITING_FOR_FRONT_PET state
Move _roundTimerSecs increment inside the 1-second tick block.
Previously it incremented every Update() call (~100ms), reaching
the 45s threshold in ~4.5 seconds instead of 45 seconds.
Matches Blizzard sniff pattern where every round wraps aura tick
processing in BEGIN(PBOID=9) / END(PBOID=9) sentinel effects.
- Emit AURA_PROCESSING_BEGIN before aura ticking
- Phase 1: DoT/HoT periodic damage/healing (SET_HEALTH)
- Phase 2: AURA_CHANGE per active aura with updated CurrentRound
- Phase 3: Decrement rounds, AURA_CANCEL for expired auras
- Emit AURA_PROCESSING_END after all aura processing
- Add explicit switch cases for effect types 13/14 in BuildRoundEffects
- Fill SourceTeam/SourcePet and Param3/Param4 on AURA_CANCEL effects
SetWeather() updated internal state but never emitted round effects,
so the client never knew weather was applied. Weather expiry also had
no AURA_CANCEL, leaving stale icons. InitialUpdate Enviros array was
never populated with active weather data.
- SetWeather() now emits AURA_APPLY targeting environment PBOID (6+)
- Previous weather is cancelled before new weather is applied
- Cleansing weather emits AURA_CANCEL for all active environment auras
- TickWeather() emits AURA_CANCEL when weather duration expires
- BuildPetBattleEnviros() populates InitialUpdate Enviros in all paths
- Added PBOID_ENVIRONMENT_BASE constant and TargetEnvSlot field for
environment-targeted round effects
The "Edit House Exterior" button sent EditorMode=4 (Customize/interior) instead
of EditorMode=6 (ExteriorCustomization). The client checks
C_HouseEditor.IsHouseEditorModeActive(ExteriorCustomization) which returned
false, so the exterior customization UI never activated.
Also the SMSG response had an empty FixtureGuid — the client uses this GUID
to determine enter vs exit state for fixture editing.
Fixes:
- Use HOUSING_EDITOR_MODE_EXTERIOR_CUSTOMIZATION (6) for fixture edit mode
- Populate FixtureGuid with the root fixture MeshObject GUID (componentType=9)
- Always CREATE Account entity in fixture mode (same pattern as decor edit)
- Include all fixture MeshObject CREATEs in same UPDATE_OBJECT packet
- Add GetPlotMeshObjects() accessor to HousingMap
The Placed Decor list was empty or incomplete because the client correlates
MeshObject FHousingDecor_C.DecorGUID with Account FHousingStorage_C entries
only when both arrive together. MeshObjects created via normal grid visibility
(separate earlier packets) arrived before FHousingStorage_C was populated,
so the client never associated them with decor entries.
Key fixes:
- Always send Account entity as CREATE (not VALUES_UPDATE) on edit mode entry
since the initial login CREATE has no FHousingStorage_C data
- Re-send CREATE for ALL decor MeshObjects in the same UPDATE_OBJECT packet
as the Account entity, ensuring the client has complete correlation data
- Add plot boundary spell visual activation on edit mode entry
- Improve edit mode diagnostics with mesh tracking counters
- Fix various housing packet and neighborhood handler improvements
Field1 and Field2 in JamCliHouseFinderNeighborhood are a bitmask of
occupied plot indices (client ORs them into uint64 at offset 520, then
checks bit N to render plot N as occupied). We were incorrectly packing
plot counts into Field1 and MapID into Field2, producing wrong bits.
Also set JamCliHouse::HouseLevel to the plot index, which the client
uses as the hash table key for plot-to-house mapping.
Fix Housing/4 NeighborhoodMirrorEntity GUID using battlenetAccountId instead
of the neighborhood's actual DB low GUID. The client matches entity GUIDs
against NeighborhoodGUID references in JamCliHouse packets — a mismatch
causes the client to fail to associate plot data with the correct entity.
Added ResetGuid() to correct the GUID in Player::LoadFromDB before the
entity is added to the world.
Fix initiative SMSG_GET_PLAYER_INITIATIVE_INFO_RESULT: duration now converts
DB2 days to seconds (×86400), progress scales from 0.0–1.0 to 0–1000 wire
format, and the packet is sent proactively on service status check so the
client's isLoaded flag gets set.
Revert incorrect HasError replacements on 14+ non-initiative response packets
back to their proper Result field assignments. Remove fake initiative SQL
hotfix data that overwrote real DB2 records.
Account and HousingPlayerHouseEntity get destroyed on the client during
map transfers to the housing map. The edit mode handler always sent
VALUES_UPDATE, which the client "rescued" (ignored) because the entities
no longer existed in its object cache.
Now check HaveAtClient() first — if the client doesn't have the entity,
send a full CREATE_OBJECT and re-register the GUID in m_clientGUIDs.
This ensures the client receives InteriorDecorPlacementBudget, storage
entries, and DecorMaxOwnedCount needed for the decor count display.
- Fix CMSG_INITIATIVE_REPORT_PROGRESS ByteBufferException: packet only
contains a packed NeighborhoodGuid (sniff-verified 7 bytes), not three
extra uint32 fields. Handler now just sends initiative info back.
- Fix 4 SMSG initiative opcode collisions (0x420369-0x42036C) with
existing opcodes (CATALOG_SHOP_OBTAIN_LICENSE, MIRROR_VARS,
SET_INSTANCE_LEAVER, UNSET_INSTANCE_LEAVER). Reassigned to
0x420380-0x420383.
- Fix tutorial mode still blocking editor: CVar injection in
Player::LoadFromDB happened after the client already fetched account
data during auth. Added SendAccountDataTimes() after SetAccountData()
to force the client to re-fetch the updated GLOBAL_CONFIG_CACHE with
housingTutorialsEnabled=0 and closedInfoFramesAccountWide bits.
- Reset FHousingStorage_C populated flag on every edit mode entry so
the Account VALUES_UPDATE always carries the full storage map.
- Remove FHousingDecorActor_C fragment from decor MeshObjects (sniff
analysis confirmed fragment 28 is not present on any retail entity).
- Use DROP+CREATE instead of CREATE IF NOT EXISTS in initiative SQL
to prevent stale schema from persisting silently.
- Add neighborhood_initiative_task_progress table persisting per-task progress
and status (NOT_STARTED/IN_PROGRESS/COMPLETE) across server restarts
- Add neighborhood_initiative_milestones table persisting milestone reached
state with timestamps
- Add neighborhood_initiative_reward_claims table tracking per-player reward
claims to prevent double-claiming
- Implement PersistSingleTaskProgress, PersistMilestoneReached, PersistRewardClaim
with corresponding CHAR_REP/INS prepared statements
- LoadFromDB now restores task progress, milestone state, and reward claims
from DB instead of defaulting to zero/recalculating
- Implement ClaimMilestoneReward with full DB2 reward chain: walks
InitiativeRewardXMilestone → InitiativeReward to grant currency, items,
or favor based on RewardType
- HasUnclaimedRewards now checks per-player claim state, not just milestone reached
- HandleGetInitiativeClaimRewardRequest and HandleGetInitiativeOpenChestRequest
now use ClaimMilestoneReward for actual reward distribution
- PersistTaskProgress stub replaced with full implementation
- Register PlayerInitiativeComponent_C entity fragment (FragmentID 37) on player
load so C_NeighborhoodInitiative Lua API returns initiative state
- Populate InitiativeInfo fields (duration, progress, milestone, cycle, contribution)
and Houses set from InitiativeManager data
- Send SMSG_INITIATIVE_SERVICE_STATUS (0x80=enabled) proactively on both exterior
and interior map entry so IsInitiativeEnabled() returns true immediately
- Set IsInitiative flag on cornerstone UI response when neighborhood has active initiative
- Add InsertSetUpdateFieldValue friend declaration to SetUpdateFieldSetter (was missing,
preventing set-type update field insertion from compiling)
- Send SMSG_HOUSING_GET_CURRENT_HOUSE_INFO_RESPONSE in interior map so editor UI
has proper house context
- Add SendPostTutorialAuras to HouseInteriorMap to unlock all editor modes (expert,
cleanup, layout, customize) — auras are lost on map transfer and must be re-sent
The client's housing editor UI checks FrameTutorialAccount flags stored in the
closedInfoFramesAccountWide CVar bitfield (GLOBAL_CONFIG_CACHE account data),
which is completely separate from the 256-bit server tutorial flags sent via
SMSG_TUTORIAL_FLAGS. Without bit 38 (HousingModesUnlocked) set in this CVar,
expert/cleanup/layout/customize modes remain locked.
Fix: inject closedInfoFramesAccountWide (all bits set) and housingTutorialsEnabled=0
into the GLOBAL_CONFIG_CACHE account data during player login and house purchase.
The code handles both fresh and existing account data, replacing or appending CVars
as needed. Account data timestamps are bumped so the client re-requests the config.
Added TODO markers for adapting this when the housing tutorial questline is
implemented (quest-driven FrameTutorialAccount bit progression).
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.
Update Account entity FHousingStorage_C when learning decor via spell so the
client's decor list refreshes immediately without requiring a relog.
Include HousingPlayerHouseEntity in the combined UPDATE_OBJECT packets sent
when entering editor mode, requesting storage, and buying a plot. The client
needs budget max values alongside FHousingStorage_C entries to compute and
display placed/remaining decor counts.
- Fix NeighborhoodMirrorData Houses array: add entries for ALL 55 plots
(including empty ones) so Houses[i] corresponds to DB2 PlotIndex=i.
Previously only occupied plots were added, causing the client to map
Houses[0] to plot 0 when it actually contained plot 7's data.
Fixed in all 5 locations: Neighborhood.cpp, NeighborhoodHandler.cpp,
HousingHandler.cpp (2 sites), Player.cpp.
- Fix door interaction GO position: raise Z from -0.56 to 1.5 (door frame
level instead of stair base) and pull X back from 9.28 to 8.0 (actual
door threshold instead of mesh origin at stair foot).
The platform WMO (574432) is already loaded from the static gameobject table
(sniff data). Dynamically spawning a second one at the ground-clamped Z
caused it to render visibly on the surface. The static spawn provides the
DynamicMapTree collision needed for house ground-clamping.
Housing/3 and Housing/4 entities were never registered in Player::m_clientGUIDs,
causing HaveAtClient() to always return false. Every SendUpdateToPlayer() call
sent CREATE_OBJECT instead of VALUES_UPDATE, producing duplicate CREATEs that
freeze/crash the client.
Include both entities in Player::BuildCreateUpdateBlockForPlayer alongside
BNetAccount, and track their GUIDs in m_clientGUIDs so subsequent updates
correctly use VALUES_UPDATE.
Split entity fragment ownership to match retail wire format:
- BNetAccount carries only FHousingStorage_C (decor catalog)
- Housing/3 entity carries FHousingPlayerHouse_C (house data)
- Housing/4 entity carries FNeighborhoodMirrorData_C (neighborhood mirror)
- Route all callers to correct entity (Housing.cpp, Neighborhood.cpp, handlers)
- Add Housing/3 and Housing/4 to player world lifecycle
Fix house spawning below ground level after DB2 position switch:
- Re-enable ground-clamping via GetHeight which includes GetGameObjectFloor
- Static platform WMO spawns (GO 574432) provide DynamicMapTree collision
- Falls back to DB2 Z when no valid ground height available
Fix decor jumping to wrong position after placement:
- World-to-local conversion now applies inverse rotation of room facing
- Previously only subtracted translation, ignoring room orientation
- Broken by atan2-based facing computation that introduced non-zero angles