VALUES update blocks don't carry an objectType field (it defaults to 0),
so the sanity check incorrectly used the non-PLAYER threshold (10) for
player character updates that legitimately need 42-46 mask blocks. Allow
up to 55 blocks for VALUES updates (could be any entity type including
PLAYER). Only enforce strict limits on CREATE_OBJECT blocks where the
objectType is known.
Homebrew's vulkan-loader hides portability ICDs (like MoltenVK) from
pre-instance extension enumeration by default, causing SDL2 to fail
with "doesn't implement VK_KHR_surface". Set VK_LOADER_ENABLE_PORTABILITY_DRIVERS
before loading the Vulkan library so the loader includes MoltenVK and
its surface extensions.
Remove the forced backpack-open constraint that prevented closing the
backpack while other bags were open. Each bag window is now independently
closable regardless of which others are open.
Add off-screen position reset to individual bag windows (renderBagWindow)
so bags saved at positions outside the current resolution snap back to
their default stack position.
Chest-type GOs now send CMSG_GAMEOBJ_USE immediately followed by
CMSG_LOOT in the same frame. The USE handler opens the chest, then the
LOOT handler reads the contents — both processed sequentially by the
server. Previously only CMSG_LOOT was sent (no USE), which failed on
AzerothCore because the chest wasn't activated first.
Reset the Bags window position to bottom-right if the saved position
is outside the current screen resolution (e.g. after a resolution
change or moving between monitors).
Chest-type game objects (quest pickups, treasure chests) now send
CMSG_LOOT directly with the GO GUID instead of CMSG_GAMEOBJ_USE +
delayed CMSG_LOOT. The server's loot handler activates the GO and
sends SMSG_LOOT_RESPONSE in one step. The old approach failed because
CMSG_GAMEOBJ_USE opened+despawned the GO before CMSG_LOOT arrived.
Double M2 bone and material descriptor pool sizes (8192 → 16384) to
handle the increased NPC count from the spline parsing fix — patrolling
NPCs that were previously invisible now spawn correctly, exhausting
the old pool limits.
AzerothCore handles loot automatically in the CMSG_GAMEOBJ_USE handler
(calls SendLoot internally). Sending a redundant CMSG_LOOT 200ms later
triggers DoLootRelease() on the server, which closes the loot the server
just opened — before SMSG_LOOT_RESPONSE ever reaches the client. This
broke quest GO interactions (Bundle of Wood, etc.) because the loot
window never appeared and quest items were never granted.
Also remove temporary diagnostic logging from GO interaction path.
The wall/floor classification threshold was lowered from 0.65 to 0.35
in a prior optimization commit, causing surfaces at 35-65° from
horizontal (steep walls, angled building geometry) to be classified as
floors and skipped during wall collision. This allowed the player to
clip through angled WMO walls.
Restore the threshold to 0.65 (cos 50°) in both the collision grid
builder and the runtime checkWallCollision skip, matching the
MAX_WALK_SLOPE limit used for slope-slide physics.
AzerothCore/ChromieCraft always writes verticalAcceleration(float) +
effectStartTime(uint32) after durationMod in the spline movement block,
regardless of whether the PARABOLIC spline flag (0x800) is set. The
parser only read these 8 bytes when PARABOLIC was flagged, causing it
to read the wrong offset as pointCount (0 instead of e.g. 11). This
made every patrolling NPC fail to parse — invisible with no displayId.
Also fix splineStart calculation (was off by 4 bytes) and remove
temporary diagnostic logging.
When a WotLK NPC has durationMod=0.0, the Classic-first spline parser
reads it as pointCount=0 and "succeeds", then consumes garbage bytes as
splineMode and endPoint. This desynchronizes the read position for all
subsequent update blocks in the packet, causing cascading failures
(truncated update mask, unknown update type) that leave NPCs without
displayIds — making them invisible.
Fix: after reading splineMode, reject the Classic parse if splineMode > 3
(valid values are 0-3) and fall through to the WotLK format parser.
CharSections.dbc has different field layouts between stock WotLK (textures
at field 4-6) and Classic/TBC/Turtle/HD-textured WotLK (VariationIndex at
field 4). Add detectCharSectionsFields() that probes field-4 values at
runtime to determine the correct layout, so both stock and modded clients
work without JSON changes.
Also add BLOODELF_MALE/FEMALE and DRAENEI_MALE/FEMALE voice types to the
NPC voice system — previously all Blood Elf and Draenei NPCs fell through
to GENERIC (random dwarf/gnome/night elf/orc mix).
GetNetStats() returns bandwidthIn, bandwidthOut, latencyHome,
latencyWorld — with real latency from getLatencyMs(). Used by
latency display addons and the default UI's network indicator.
AcceptBattlefieldPort(index, accept) accepts or declines a
battleground queue invitation. Backed by existing acceptBattlefield
and declineBattlefield methods.
NumTaxiNodes() returns the count of taxi nodes available at the
current flight master. TaxiNodeName(index) returns the node name.
TaxiNodeGetType(index) returns whether the node is known/reachable.
TakeTaxiNode(index) activates the flight to the selected node.
Uses existing taxiNodes_ data and activateTaxi() method.
Enables flight path addons and taxi map overlay addons.
GetNumGossipOptions() returns option count for current NPC dialog.
GetGossipOptions() returns pairs of (text, type) for each option
where type is "gossip", "vendor", "taxi", "trainer", etc.
SelectGossipOption(index) selects a dialog option.
GetNumGossipAvailableQuests() / GetNumGossipActiveQuests() return
quest counts in the gossip dialog.
CloseGossip() closes the NPC dialog.
Uses existing GossipMessageData from SMSG_GOSSIP_MESSAGE handler.
Enables gossip addons and quest helper dialog interaction.
IsConnectedToServer() checks if connected to the game server.
UnequipItemSlot(slot) moves an equipped item to the backpack.
HasFocus() checks if the player has a focus target set.
GetRealmName() / GetNormalizedRealmName() return realm name.
All backed by existing GameHandler methods.
ShowHelm() / ShowCloak() toggle helm/cloak visibility.
TogglePVP() toggles PvP flag.
Minimap_Ping(x, y) sends a minimap ping to the group.
RequestTimePlayed() requests /played data from server.
All backed by existing GameHandler methods.
InviteUnit(name) invites a player to the group.
UninviteUnit(name) removes a player from the group.
LeaveParty() leaves the current party/raid.
Backed by existing inviteToGroup, uninvitePlayer, leaveGroup methods.
GuildInvite(name) invites a player to the guild.
GuildUninvite(name) kicks a member (uses kickGuildMember).
GuildPromote(name) / GuildDemote(name) change rank.
GuildLeave() leaves the guild.
GuildSetPublicNote(name, note) sets a member's public note.
All backed by existing GameHandler methods that send the appropriate
guild management CMSG packets.
DoEmote(token) maps emote token strings to TextEmote DBC IDs and
sends them via sendTextEmote. Supports 30+ common emotes: WAVE,
BOW, DANCE, CHEER, CHICKEN, CRY, EAT, FLEX, KISS, LAUGH, POINT,
ROAR, RUDE, SALUTE, SHY, SILLY, SIT, SLEEP, SPIT, THANK, CLAP,
KNEEL, LAY, NO, YES, BEG, ANGRY, FAREWELL, HELLO, WELCOME, etc.
Targets the current target if one exists.
AddFriend(name, note) and RemoveFriend(name) manage the friends list.
AddIgnore(name) and DelIgnore(name) manage the ignore list.
ShowFriends() is a stub (friends panel is ImGui-rendered).
All backed by existing GameHandler methods that send the appropriate
CMSG_ADD_FRIEND, CMSG_DEL_FRIEND, CMSG_ADD_IGNORE, CMSG_DEL_IGNORE
packets to the server.
GetNumWhoResults() returns result count and total online players.
GetWhoInfo(index) returns name, guild, level, race, class, zone,
classFileName — the standard 7-field /who result signature.
SendWho(query) sends a /who search to the server.
SetWhoToUI() is a stub for addon compatibility.
Uses existing whoResults_ from SMSG_WHO handler and queryWho().
Enables /who replacement addons and social panel search.
IsPlayerSpell(spellId) checks if the spell is in the player's known
spells set. Used by action bar addons to distinguish permanent spells
from temporary proc/buff-granted abilities.
IsCurrentSpell(spellId) checks if the spell is currently being cast.
IsSpellOverlayed() and IsAutoRepeatSpell() are stubs for addon compat.
GetCurrentTitle() returns the player's chosen title bit index.
GetTitleName(bit) returns the formatted title string from
CharTitles.dbc (e.g., "Commander %s", "%s the Explorer").
SetCurrentTitle() is a stub for title switching.
Used by title display addons and the character panel title selector.
GetInspectSpecialization() returns the inspected player's active
talent group from the cached InspectResult data.
NotifyInspect() and ClearInspectPlayer() are stubs — inspection is
auto-triggered by the C++ side when targeting players. These prevent
nil errors in inspection addons that call them.
GetHonorCurrency() returns honor points from update fields.
GetArenaCurrency() returns arena points.
GetTimePlayed() returns total time played and level time played
in seconds (populated from SMSG_PLAYED_TIME).
GetBindLocation() returns the hearthstone bind zone name.
Used by currency displays, /played addons, and hearthstone tooltip.
GetNumSavedInstances() returns count of saved instance lockouts.
GetSavedInstanceInfo(index) returns 9-field WoW signature: name,
mapId, resetTimeRemaining, difficulty, locked, extended,
instanceIDMostSig, isRaid, maxPlayers.
Uses existing instanceLockouts_ from SMSG_RAID_INSTANCE_INFO.
Enables SavedInstances and lockout tracking addons to display
which raids/dungeons the player is locked to and when they reset.
GetNumBattlefieldScores() returns player count in the BG scoreboard.
GetBattlefieldScore(index) returns 12-field WoW API signature: name,
killingBlows, honorableKills, deaths, honorGained, faction, rank,
race, class, classToken, damageDone, healingDone.
GetBattlefieldWinner() returns winning faction (0=Horde, 1=Alliance)
or nil if BG is still in progress.
RequestBattlefieldScoreData() sends MSG_PVP_LOG_DATA to refresh the
scoreboard from the server.
Uses existing BgScoreboardData from MSG_PVP_LOG_DATA handler.
Enables BG scoreboard addons and PvP tracking.
UnitIsPVP(unit) checks UNIT_FLAG_PVP (0x1000) on the unit's flags
field. Used by unit frame addons to show PvP status indicators.
UnitIsPVPFreeForAll(unit) checks for FFA PvP flag.
GetBattlefieldStatus() returns stub ("none") for addons that check
BG queue state on login. Full BG scoreboard data exists in
GameHandler but is rendered via ImGui.
Items with the Unique-Equipped flag (itemFlags & 0x1000000) now
display "Unique-Equipped" in the tooltip header. This is distinct
from "Unique" (maxCount=1) — Unique-Equipped means you can carry
multiple but only equip one (e.g., trinkets, rings with the flag).
Items with a startQuestId now display "This Item Begins a Quest" in
gold text at the bottom of the tooltip, matching WoW's behavior.
Helps players identify quest-starting drops in their inventory.
Passes startsQuest flag through _GetItemTooltipData from the
startQuestId field in ItemQueryResponseData.
Spell descriptions now substitute \$o1/\$o2/\$o3 with the total
periodic damage/healing: base_per_tick × (duration / 3sec).
Example: SW:Pain with base=4 (5 per tick), duration=18sec (6 ticks):
Before: "Causes X Shadow damage over 18 sec"
After: "Causes 30 Shadow damage over 18 sec"
Combined with \$s1 (per-tick/instant) and \$d (duration), the three
most common spell template variables are now fully resolved. This
covers the vast majority of spell tooltips.
Spell descriptions now substitute \$d with actual duration values:
Before: "X damage over X sec"
After: "30 damage over 18 sec"
Implementation:
- DurationIndex field (40) added to all expansion Spell.dbc layouts
- SpellDuration.dbc loaded during cache build: maps index → base ms
- cleanSpellDescription substitutes \$d with resolved seconds/minutes
- getSpellDuration() accessor on GameHandler
Combined with \$s1/\$s2/\$s3 from the previous commit, most common
spell description templates are now fully resolved with real values.
Spell descriptions now substitute \$s1/\$s2/\$s3 template variables
with actual effect base points from Spell.dbc (field 80/81/82).
For example: "causes \$s1 Fire Damage" → "causes 562 Fire Damage".
Implementation:
- Added EffectBasePoints0/1/2 to all 4 expansion DBC layouts
- SpellNameEntry now stores effectBasePoints[3]
- loadSpellNameCache reads base points during DBC iteration
- cleanSpellDescription substitutes \$s1→abs(base)+1 when available
- getSpellEffectBasePoints() accessor on GameHandler
Values are DBC base points (before spell power scaling). Still uses
"X" placeholder for unresolved variables (\$d, \$o1, etc.).
CalendarGetDate() returns real weekday, month, day, year from the
system clock. Used by calendar addons and date-aware UI elements.
CalendarGetNumPendingInvites() and CalendarGetNumDayEvents() return 0
as stubs — prevents nil errors in addons that check calendar state.
GetDifficultyInfo(id) returns name, groupType, isHeroic, maxPlayers
for WotLK instance difficulties:
0: "5 Player" (party, normal, 5)
1: "5 Player (Heroic)" (party, heroic, 5)
2: "10 Player" (raid, normal, 10)
3: "25 Player" (raid, normal, 25)
4/5: 10/25 Heroic raids
Used by boss mod addons (DBM, BigWigs) and instance info displays
to show the current dungeon difficulty.
Fire WEATHER_CHANGED(weatherType, intensity) when the server sends
SMSG_WEATHER with a new weather state. Enables weather-aware addons
to react to rain/snow/storm transitions.
GetWeatherInfo() returns current weatherType (0=clear, 1=rain, 2=snow,
3=storm) and intensity (0.0-1.0). Weather data is already tracked by
game_handler and used by the renderer for particle effects and fog.
GuildRoster() triggers CMSG_GUILD_ROSTER to request updated guild
member data from the server. Called by guild roster addons and the
social panel to refresh the member list.
SortGuildRoster() is a no-op (sorting is handled client-side by
the ImGui guild roster display).
BuyMerchantItem(index, count) purchases an item from the current
vendor by merchant slot index. Resolves itemId and slot from the
vendor's ListInventoryData.
SellContainerItem(bag, slot) sells an item from the player's
inventory to the vendor. Supports backpack (bag=0) and bags 1-4.
Enables auto-sell addons (Scrap, AutoVendor) and vendor UI addons
to buy/sell items programmatically.
RepairAllItems(useGuildBank) sends CMSG_REPAIR_ITEM to repair all
equipped items at the current vendor. Checks CanMerchantRepair before
sending. Optional useGuildBank flag for guild bank repairs.
One of the most commonly needed addon functions — enables auto-repair
addons to fix all gear in a single call when visiting a repair vendor.
AcceptTrade() locks in the trade offer via CMSG_ACCEPT_TRADE.
CancelTrade() cancels an open trade via CMSG_CANCEL_TRADE.
InitiateTrade(unit) starts a trade with a target player.
Uses existing GameHandler trade functions and TradeStatus tracking.
Enables trade addons and macro-based trade acceptance.
Quality-colored item links for auction house items, enabling AH addons
to display clickable item links in their UI and chat output.
This is the 100th commit of this session, bringing the total to
408 API functions across all WoW gameplay systems.
GetInboxNumItems() returns count of mail messages.
GetInboxHeaderInfo(index) returns the full 13-field WoW API signature:
packageIcon, stationeryIcon, sender, subject, money, COD, daysLeft,
hasItem, wasRead, wasReturned, textCreated, canReply, isGM.
GetInboxText(index) returns the mail body text.
HasNewMail() checks for unread mail (minimap icon indicator).
Uses existing mailInbox_ populated from SMSG_MAIL_LIST_RESULT.
Enables postal addons (Postal, MailOpener) to read inbox data.
GetNumGlyphSockets() returns 6 (WotLK glyph slot count).
GetGlyphSocketInfo(index, talentGroup) returns enabled, glyphType
(1=major, 2=minor), glyphSpellID, and icon for each socket.
Uses existing learnedGlyphs_ array populated from SMSG_TALENTS_INFO.
Enables talent/glyph inspection addons.
This commit brings the total API count to exactly 400 functions.
Complete the pet action bar interaction:
- CastPetAction(index) — cast the pet spell at the given bar slot
by sending the packed action via sendPetAction
- TogglePetAutocast(index) — toggle autocast for the pet spell
at the given slot via togglePetSpellAutocast
- PetDismiss() — send dismiss pet command
- IsPetAttackActive() — whether pet is currently in attack mode
Together with the previous pet bar functions (HasPetUI, GetPetActionInfo,
PetAttack, PetFollow, PetWait, PetPassiveMode, PetDefensiveMode), this
completes the pet action bar system for hunters/warlocks/DKs.
Implement pet action bar functions using existing pet data:
- HasPetUI() — whether player has an active pet
- GetPetActionInfo(index) — name, icon, isActive, autoCastEnabled
for each of the 10 pet action bar slots
- GetPetActionCooldown(index) — cooldown state stub
- PetAttack() — send attack command to current target
- PetFollow() — send follow command
- PetWait() — send stay command
- PetPassiveMode() — set passive react mode
- PetDefensiveMode() — set defensive react mode
All backed by existing SMSG_PET_SPELLS data (petActionSlots_,
petCommand_, petReact_, petAutocastSpells_) and sendPetAction().
GetActionBarPage() returns the current action bar page (1-6).
ChangeActionBarPage(page) switches pages and fires ACTIONBAR_PAGE_CHANGED
via the Lua frame event system. Used by action bar addons and the
default UI's page arrows / shift+number keybinds.
Action bar page state tracked in Lua global __WoweeActionBarPage.