PlaceDecor and RemoveDecor previously only modified in-memory state,
relying on SaveToDB at logout for persistence. A server crash would
lose all placed/removed decor changes since last save.
Add crash-safe immediate persistence:
- PlaceDecor: INSERT decor row + UPDATE catalog count on placement
- RemoveDecor: DELETE decor row + UPDATE catalog count on removal
- New prepared statements: CHAR_UPD_CHARACTER_HOUSING_CATALOG_COUNT,
CHAR_DEL_CHARACTER_HOUSING_DECOR_SINGLE
Spawn the physical house structure GameObject when a player buys a plot,
using the DB2 PlotGameObjectID at the plot's HousePosition. Persist
player-chosen house positions to the database so they survive relogs.
Fix the SetHousePosition handler to actually store and apply the position
instead of being a no-op.
Spawn placed decor items as visible GameObjects with the FHousingDecor_C
entity fragment (InitHousingDecorData), following the cornerstone pattern.
Hook decor GO spawn/move/despawn into the Place/Move/Remove handlers so
furniture appears and updates in real time.
Fix outdoor decor budget enforcement: decor with roomGuid=0 now routes
to the exterior budget instead of always counting as interior.
Fix edit mode response wire format to match Fixture/Room pattern
(uint32 Result + Bit Active).
Set default _houseType to 32 (sniff-verified) instead of 0.
Fix SMSG_HOUSING_DECOR_SET_EDIT_MODE_RESPONSE wire format - the uint8
between DecorCount and DecorGuids is a HousingResult status code (0=SUCCESS,
1=ACTION_LOCKED_BY_COMBAT), not an Active boolean. Writing 1 caused the
client to show "You can't do that while in combat" even when not in combat.
Fix NeighborhoodGuid mismatch between client-supplied DB2 ID and server
canonical counter by using neighborhood->GetGuid() consistently.
Fix JamCurrentHouseInfo GUID field swap (SecondaryOwnerGuid/PlotGuid).
Add Player::UpdateHousingMapId() to update PlayerMirrorHouse.MapID when
entering housing maps, using snapshot-clear-rebuild pattern to work around
DynamicUpdateField PublicSet=false restriction.
Add GUID mismatch fallback in HousingMap::AddPlayerToMap and proactive
SMSG_NEIGHBORHOOD_PLAYER_ENTER_PLOT send.
- Fix permissions handler: CMSG sends HouseGuid not PlayerGuid, compare
against housing->GetHouseGuid() to detect owner (was always returning
visitor permissions due to GUID type mismatch)
- Fix EditMode response wire format to match sniff: HouseGuid + PlotGuid
+ uint8 Active + uint32 Status + optional OwnerGuid (was uint32 + bit)
- Immediately persist housing to DB on creation so data survives restarts
- Update PlayerHouseInfoComponentData::Houses UpdateField mid-session so
dashboard works without relogging after purchase
Correct multiple packet format mismatches preventing the housing dashboard
from populating after buying a plot:
- Fix HouseStatusResponse wire format: 3 PackedGUIDs + uint32 (was 4 + 2 uint8s)
- Fix PlayerHousesInfoResponse to use JamCurrentHouseInfo with correct field
mapping: OwnerGuid=HouseGUID, SecondaryOwnerGuid=PlotGUID,
PlotGuid=NeighborhoodGUID, Flags=PlotIndex, HouseTypeId=32
- Add PlotGUID generation (HighGuid::Housing subType=2) to Housing class
- Send FirstTimeDecorAcquisition packets for starter decor items after purchase
- Send 2x UpdateHousesLevelFavor with 36-byte format after purchase
- Fix owner permissions to 0xE0 (bits 5,6,7) instead of 0xFF
- Add serverside spell 1266097 for cornerstone UILink click handling
- Fix cornerstone GO interaction to cast spell instead of direct handler call
- Add createTime tracking to Housing for optional HouseId field
- Register housing spell script and new opcode handlers
The client's 12.0 deserializer for SMSG_NEIGHBORHOOD_OPEN_CORNERSTONE_UI_RESPONSE
expects: uint32 Result, PackedGUID NeighborhoodGuid, PackedGUID PlotGuid,
uint64 Cost, uint8 PlotIndex, uint32+bytes NameString, uint8 OptionalFlags.
The previous implementation was missing the second PackedGUID (PlotGuid),
the length-prefixed neighborhood name string, and the optional flags byte.
Without the second GUID, the client read uint64(Cost) as a PackedGUID mask
byte, corrupting the entire packet parse and causing the JAM deserializer
to silently fail. The OPEN_PLOT_CORNERSTONE Lua event never fired, so the
purchase UI never appeared.
- Fix type effectiveness matrix (9/10 rows had wrong strong/weak, 0.67->0.66)
- Fix state machine enum to match client (add CREATED_FAILED, FINAL_ROUND)
- Add PetBattleEffectFlags (miss/crit/heal/immune/strong/weak) and populate them
- Add post-battle health sync back to journal with 50% auto-heal
- Add missing effect types (REPLACE_PET, OVERRIDE_ABILITY, WORLD_STATE_UPDATE)
- Implement GetTrapStatus() with full validation (species, health, journal)
- Add PetBattlePetStatusFlags and compute from pet state
- Add PetBattleInputFlags (ability/swap locked, waiting for pet)
- Add ability lockdown tracking alongside cooldowns
- Add critical hit system (5% base, 1.5x multiplier) via DamageResult struct
- Expand PetBattleRequestFailReason to 24 values matching client
- Add max game length warning (5 min before timeout)
- Implement NPC trainer battles (LoadNPCTeams from world DB, CreateNPCBattle,
InitNPCBattle, GenerateNPCTeamInput with enhanced AI)
- Add battle_pet_npc_team SQL schema
- Complete PvP flow: full validation, accept/decline, proper queue status codes
- Add PvP forfeit penalty (Pet Battle Deserter debuff)
- Add SMSG_BATTLE_PETS_HEALED and SMSG_BATTLE_PET_TRAP_LEVEL packets
- Register CMSG_BATTLE_PET_UPDATE_DISPLAY_NOTIFY handler
- Add boss pet handling (damage cap at 35% max HP, not capturable)
- Add aura state flags (JUST_APPLIED on creation, INFINITE for permanent)
- Add achievement criteria (WinPetBattle, LosePetBattle, capture)
The StartTutorial handler was sending HouseStatus=1 (active house)
with populated owner GUIDs but no HouseGuid and PlotIndex=0xFF.
The client interprets HouseStatus=1 as "player owns a house," which
prevents the Cornerstone "For Sale" purchase UI from displaying —
the client thinks the player is already a homeowner.
During the tutorial the player has no house yet, so send the default
empty response (HouseStatus=0, all GUIDs empty). The neighborhood
context is already provided by SMSG_HOUSING_GET_CURRENT_HOUSE_INFO_RESPONSE
when the player enters the HousingMap.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
The no-house path was setting OwnerBNetGuid and OwnerPlayerGuid in
SMSG_HOUSING_HOUSE_STATUS_RESPONSE even though HouseGuid was empty
and HouseStatus was 0. This is inconsistent — a response with no
house should have all-empty GUIDs and PlotIndex=0xFF (the default).
Neighborhood context is already provided separately via
SMSG_HOUSING_GET_CURRENT_HOUSE_INFO_RESPONSE sent in
HousingMap::AddPlayerToMap().
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Two fixes to enable Cornerstone click interaction:
1. Send SMSG_HOUSING_GET_CURRENT_HOUSE_INFO_RESPONSE proactively in
HousingMap::AddPlayerToMap() so the client can call
SetViewingNeighborhood() and populate its global housing context.
Without this, GetCornerstoneNeighborhoodInfo() returns empty data
and the Cornerstone purchase UI cannot display.
2. Add SQL migration for AreaTrigger entry 37358 (housing plot AT)
with ScriptName='at_housing_plot', Shape=Sphere, radius=40 yards.
Without this in the database, CreateStaticAreaTrigger() silently
fails and SMSG_NEIGHBORHOOD_PLAYER_ENTER_PLOT never fires.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Retail packet sniff analysis proves that ALL housing plots use a single
Cornerstone GameObject (entry 457142, DisplayID 110660, Type 48/UILink)
with GOState toggling to communicate ownership:
- GOState 0 (ACTIVE) = For Sale / unoccupied
- GOState 1 (READY) = Owned / occupied
- All Cornerstones have Flags=32 (GO_FLAG_NODESPAWN)
The previous implementation spawned a fabricated "For Sale" sign
(entry 417487, DisplayID 8206) for unoccupied plots. DisplayID 8206 is
a vanilla-era model that renders as a flat brown rectangle.
Changes:
- SpawnPlotGameObjects: Always use CornerstoneGameObjectID, set GOState
based on ownership, apply GO_FLAG_NODESPAWN
- Replace SwapPlotGameObject (destroy/recreate) with SetPlotOwnershipState
(GOState toggle on existing Cornerstone)
- Simplify all 3 callers (ReservePlot, BuyHouse, EvictPlot) to pass
bool ownership instead of GO entry IDs
- Remove fabricated entry 417487 from world SQL
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Adds exteriorLocked, houseSize, and houseType columns to existing
character_housing tables via ALTER TABLE (the schema file was updated
in the prior commit but existing databases need this migration).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
The 'maps' logger is at WARNING level by default, suppressing
all DEBUG messages. Route HousingMap and MapManager neighborhood
logging to the 'housing' channel which is at TRACE level and
writes to Housing.log.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Creature templates, spawns, models, gameobject templates, spawns,
gossip menus, vendor inventories, quest data, and trainer spells
for the Alliance housing neighborhood (The Aerie).
Column names adapted to current TrinityCore creature_template schema
(type/family/Classification, 64-bit npcflag).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Track plot GameObjects in HousingMap so for-sale-signs can be swapped
to cornerstones on purchase and reverted on eviction. Previously the
visual GO never changed after PurchasePlot(), leaving stale for-sale
signs on owned plots.
Fix HouseFinder neighborhood detail to show all 55 DB2 plots with
availability and cost instead of only listing occupied plots, so the
client can render the full plot grid.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
Wire format fixes:
- Split HouseStatusResponse.Status (uint32) into HouseStatus/PlotIndex/StatusFlags sub-fields
- Rename ResidentEntry.PlotIndex to StatusFlags (carries flag data, not plot index)
- Rename NeighborhoodPlayerEnterPlot.PlayerGuid to PlotAreaTriggerGuid (sends AT GUID)
Data structure optimization:
- Convert Neighborhood._plots from vector to fixed array<55> for O(1) plot access
- Add PlotInfo.IsOccupied() sentinel check, GetOccupiedPlotCount() helper
- Update all 8 callers across handlers, managers, and Player mirror data sync
Roster enhancement:
- Add HouseGuid as first field in roster member wire format (before PlayerGuid)
- Populate from plot data in both handler and Player roster builders
AreaTrigger integration:
- Add AreaTrigger::CreateStaticAreaTrigger() factory for non-caster ATs
- Add AreaTrigger::InitHousingPlotData() for HousingPlotAreaTriggerData fragment
- Spawn plot AreaTriggers (entry 37358) at HousePosition in HousingMap::LoadGridObjects
- Add SQL for areatrigger_create_properties (sphere, 40yd radius)
Plot aura system:
- Add at_housing_plot AreaTriggerAI script for plot enter/exit detection
- Apply own-plot aura (1266699) or visiting-plot aura (1239847) on AT enter
- Send SMSG_NEIGHBORHOOD_PLAYER_ENTER/LEAVE_PLOT packets
Multi-house architecture:
- Replace single _housing with vector<unique_ptr<Housing>> _housings
- Context-aware GetHousing() resolves via current HousingMap neighborhood
- Add GetHousingForNeighborhood(), GetAllHousings(), updated CreateHousing/DeleteHousing
- Update all handlers and HousingMap for multi-house semantics