- send CMSG_BUY_ITEM as vendorGuid + itemId + count (drop extra slot/bag fields)
- reset vendor list state before parsing SMSG_LIST_INVENTORY to prevent stale items carrying over
- add packet length guards for list-inventory header and per-item rows
- keep optional extended-cost parsing for cross-core compatibility
Batches whose named texture fails to load now render invisible instead of
white (the swampreeds01a.blp case causing a white shell around aquatic plants).
Also implements proper M2 opacity plumbing:
- Parse texture weight tracks (M2Track<fixed16>) and color animation alpha
tracks (M2Color.alpha) to resolve per-batch opacity at load time
- Skip batches with batchOpacity < 0.01 in the render loop
- Apply M2Texture.flags (bit0=WrapS, bit1=WrapT) to GL sampler wrap mode
- Upload both UV sets (texCoords[0] and texCoords[1]) and select via
textureUnit uniform, so batches referencing UV set 1 render correctly
PushID(item.slot) causes conflicting IDs when the server sends items with
duplicate or zero slot values. Use the loop index instead, which is always
unique regardless of what the server puts in the slot field.
Add AssetManager hookup to AudioEngine so the path-based playSound2D/3D
overloads can load files on demand rather than requiring preloading.
- Add setAssetManager() to AudioEngine (called during world load alongside
other audio manager initializations)
- playSound2D(mpqPath) now calls assetManager->readFile() then delegates
to the vector<uint8_t> overload (removes the "not yet implemented" warning)
- playSound3D(mpqPath, position) same — delegates to the fully spatialized
vector overload (was previously silently falling back to 2D)
TrinityCore's HandleBuyItemOpcode reads vendorGuid → item → slot → count → bagIndex.
The previous fix had accidentally reversed item and slot, so the server received
the vendor slot number as the item ID (a small number like 1-5) and the actual
item ID as the slot, causing every purchase to be silently rejected.
The parser used wrong SplineFlag bitmask values that don't match WotLK 3.3.5a:
- Animation: 0x00000100 → 0x00400000 (was matching SPLINEFLAG_DONE)
- Parabolic: 0x00000200 → 0x00000800 (was matching SPLINEFLAG_FALLING)
- Uncompressed path: 0x00040000 → Catmullrom|Flying (0x00082000)
The critical bug: SPLINEFLAG_FALLING (0x00000200) is set when NPCs move over
sloped terrain during combat. The parser mistook it for parabolic and read 8
extra bytes, misaligning pointCount and the destination coords. hasDest stayed
false, the move callback never fired, and NPCs appeared frozen in place.
Also fix Animation field read: uint8+int32 (5 bytes) not uint32+uint32 (8 bytes).
Auto-detect whether SMSG_LIST_INVENTORY has 7 fields (28 bytes/item, no
extendedCost) or 8 fields (32 bytes/item) per item from packet size. Servers
that omit extendedCost caused every item after the first to have garbage prices
due to misaligned field reads.
Also remove the [+Tokens] hybrid indicator; only show [Tokens] on pure
token-purchased items (buyPrice==0 && extendedCost!=0).
- Trigger ding when UNIT_FIELD_LEVEL increases for player: plays LevelUp sound,
cheer emote animation, and shows screen overlay
- renderDingEffect(): 3 expanding golden rings + "LEVEL X!" / "DING!" text
drawn via ImDrawList foreground (3s duration, fades out last 0.8s)
- triggerDing() wires sound (UiSoundManager::playLevelUp) + emote + overlay
- LevelUpCallback in GameHandler fires on genuine level increase (newLevel > oldLevel)
- "Test: Level Up" button in escape menu triggers ding at current player level
- Fix SMSG_QUESTGIVER_REQUEST_ITEMS: read emoteDelay(u32)+emoteId(u32)+autoFinish(u8)
instead of 5 uint32s — the 11-byte over-read corrupted requiredMoney, itemCount,
and all item data (itemId/count/displayInfoId)
- Fix garbled CSV fallback in asset_manager: return nullptr instead of silently
returning garbled DBC data when binary fallback is unavailable
- Add NPC spawn diagnostics: log when UNIT blocks have displayId=0 (wrong field index)
and when spawn callback fires with displayId + position
- Improve getModelPathForDisplayId failure logging: distinguish displayDataMap_ miss
vs modelIdToPath_ miss, and include map sizes for context
Checkbox in Settings > Gameplay > Loot section. When enabled, all items
are automatically sent CMSG_AUTOSTORE_LOOT_ITEM on loot response (same
as right-click looting each item). Gold always auto-loots regardless.
Setting persists in settings.cfg as auto_loot=0/1.
- Mute button: small [M] button alongside minimap zoom controls, turns red when active; directly sets AudioEngine master volume to 0, restores on unmute; persists in settings.cfg
- Original Soundtrack: checkbox in Settings > Audio that controls whether custom original music tracks (file: prefix) are included in zone music rotation; when disabled, only WoW MPQ tracks play; persists in settings.cfg
- ZoneManager.getRandomMusic() now filters file: paths when OST is disabled, falling back to full list if zone has no non-file tracks
- Handle uppercase \$N in replaceGenderPlaceholders (WoW servers send \$N for player name, we only handled \$n)
- Fix SMSG_QUESTGIVER_REQUEST_ITEMS required item field order: packet sends [displayInfoId, count, itemId] not [itemId, count, displayInfoId], causing wrong items to display (e.g. Ring of Pure Silver instead of Tough Wolf Meat)
The gossip questIcon field is an integer enum (2=available, 4=incomplete,
5=ready-to-turn-in), not a bitmask. Using (questIcon & 0x04) caused icon=4
(in-progress quests) to be treated as completable, so the client sent
CMSG_QUESTGIVER_REQUEST_REWARD for incomplete quests. The server rejected
these silently and re-sent SMSG_GOSSIP_MESSAGE, causing an infinite loop.
Fix: use exact equality (questIcon == 5 for completable, == 4 for incomplete).
Also register SMSG_CANCEL_AUTO_REPEAT (0x06B) to suppress the unhandled
opcode warning logged on every login.
Root cause: OpcodeTable::loadFromJson() cleared all mappings before
loading the expansion JSON, so any WotLK opcode absent from Turtle WoW's
opcodes.json (including SMSG_AURA_UPDATE and SMSG_AURA_UPDATE_ALL) was
permanently lost. Changed loadFromJson to patch/merge on top of existing
defaults so only explicitly listed opcodes are overridden.
Also fix isBuff border color: was testing flag 0x02 (effect 2 active)
instead of 0x80 (negative/debuff flag).
Add client-side duration countdown: AuraSlot.receivedAtMs is stamped
when the packet arrives; getRemainingMs(nowMs) subtracts elapsed time so
buff tooltips show accurate remaining duration instead of stale snapshot.
The action bar's inline Spell.dbc loader had a broken field index lookup:
operator[] on the layout map would silently insert 0 if 'Name' wasn't a
key, causing all names to be read from field 0 (the spell ID). Also the
once-only 'attempted' flag meant if assetMgr wasn't ready, it never retried.
Replace the duplicated broken DBC loading in the action bar and buff bar
with SpellbookScreen::lookupSpellName(), which reuses the spellbook's
already-correct loading (expansion layout + WotLK fallback). Remove the
now-unused actionSpellNames/actionSpellDbAttempted/actionSpellDbLoaded fields.
Re-send CMSG_ATTACKSWING every second while auto-attacking so combat
resumes automatically when the server pauses the attack loop (out of
range, etc). Previously the client kept autoAttacking=true but never
re-engaged, requiring the player to manually right-click again.
Also remove leftover single-player/offline references from comments.
SMSG_ATTACKERSTATEUPDATE, SMSG_SPELLNONMELEEDAMAGELOG, and SMSG_SPELLDAMAGELOG
were showing combat text for all combat events in the zone, not just the
player's own fights. Added early-return in all three handlers when neither
the attacker nor target is the player.
Outgoing events (player attacks enemy: damage dealt, Dodge/Parry when enemy
evades) now float on the right side of screen (near target) instead of above
the player character. Incoming events (enemy attacks player) remain at
center-left (near player).
Dodge/Parry labels now show direction: "Dodge"/"Parry" when the enemy evades
the player's attack (outgoing, gray), and "You Dodge"/"You Parry" when the
player evades an incoming attack (cyan). Crit damage now has distinct colors
for outgoing (bright yellow) vs incoming (orange).
- audio_engine.cpp: cache key was based on vector pointer address, not
content — cache never hit since each asset load produces a new
allocation. Replace with FNV-1a over head+tail bytes + size, giving
correct content-based identity at O(1) cost. Add 256-entry cap with
eviction to prevent unbounded growth over long sessions.
- game_handler.hpp/cpp, spellbook_screen, game_screen: knownSpells was
a std::vector with O(n) std::find lookups scattered across packet
handlers and UI code. Switch to std::unordered_set for O(1) insert,
erase, and membership checks. Update all push_back/erase-remove/find
call sites to use set operations.
Turtle/Classic Spell.csv files are garbled (field 0 is a string rather
than a numeric ID) so loadDBC() falls back to the WotLK binary Spell.dbc
(234 fields). getSpellIcon() was still using the Turtle layout (IconID at
field 117) to read the WotLK binary, producing garbage values and no icons.
When the loaded Spell.dbc has >=200 fields it is the WotLK binary; use the
WotLK IconID field (133) directly, matching the same approach already used
in spellbook_screen.cpp.
Previously this opcode was unrecognised and silently dropped, leaving the
client stuck in ENTERING_WORLD with no feedback when the server rejected a
login (duplicate session, world down, disabled race/class, etc.). Now we
decode the LoginFailureReason byte, reset state to CHAR_LIST_RECEIVED so
the player can retry, and surface a red error message on the character screen
via the new CharLoginFailCallback. Also adds isError colour support to
CharacterScreen::setStatus so failures show in red and successes in green.
- Add TurtlePacketParsers with dedicated movement block parser (Classic format + transport timestamp)
- Fix quest giver status: read uint32 and translate vanilla enum values for Classic/Turtle
- Fix quest accept packet: remove trailing uint32 that vanilla servers reject
- Fix quest details parser: auto-detect vanilla vs WotLK format (informUnit field)
- Fix spellbook and action bar icons: fallback to WotLK DBC field indices when expansion layout fails
- Fix spell cast failure messages: translate vanilla SpellCastResult codes (+1 offset)
- Fix realm list: correct type values (6=RP, 8=RP-PvP) and population thresholds
- Fix music: disable looping for zone music, auto-advance to next random track when finished
- Add music anti-repeat: avoid playing the same track back-to-back
- Make TBC update block parsing resilient (keep parsed blocks on failure instead of aborting)
- Add right-click attack on hostile mobs
- Add name query diagnostic logging
CMSG_CHAR_CREATE was silently dropped by the server because the packet
was 4 bytes too short. Wireshark capture of the real 1.12.1 client
revealed 4 trailing zero bytes after outfitId. Also treat IN_PROGRESS
(code 46) as success since Turtle WoW sends it instead of SUCCESS.
- Fix action bar, bag bar, chat window to track window resize (Always pos)
- Show spell names in cast bar instead of spell IDs
- Play precast/cast-complete sounds via SpellSoundManager
- Fix hearthstone to use CMSG_CAST_SPELL directly (avoids slot sync issues)
- Show map name instead of coordinates in hearthstone tooltip
- Show cooldown time remaining in action bar tooltips
- Search equipped slots and bags for action bar item icons
- Redesign realm screen: back button, larger table/buttons, auto-select
realm with characters, double-click to enter, proportional columns
The binary DBC files for all expansions use the same field ordering
(VariationIndex=4, ColorIndex=5, Texture1=6), but classic/tbc/turtle
dbc_layouts.json had swapped texture and variation/color fields, causing
all skin/face/hair/underwear lookups to fail. Also adds generalized
NxN texture scaling and a second video to README.
- Clear introActive/idleOrbit in externalFollow block so mouse panning works during taxi
- Skip full world reload on same-map teleports (taxi landing) by tracking loadedMapId
- Collect all model IDs for a path when resolving gryphon display ID (fixes displayId=0)
- Remove incorrect MountDisplayId fields from Vanilla/Turtle TaxiNodes DBC layouts
- Add Vanilla fly animation IDs (234/229/233) to taxi mount animation candidates
VMaNGOS reads uint32 entry + uint64 guid for CMSG_ITEM_QUERY_SINGLE,
same as WotLK. Removing the GUID caused the server to silently drop
all item queries, leaving equipment with no names or display info.
Replace the "not supported" error message with actual pickup from
equipment slots. Placing equipment items into backpack or bag slots
sends CMSG_SWAP_ITEM to the server.
Use FlyForward/FlyIdle animations (IDs 158/159) for taxi mounts instead
of ground movement animations (Stand/Run). Add detailed mount texture
debug logging and improve fallback texture resolution for gryphon/wyvern
models with multiple skin slots.
Vanilla SMSG_MESSAGECHAT writes senderGuid twice for all standard chat
types. Parser only read one GUID for WHISPER/WHISPER_INFORM, shifting
the read position and causing the server echo filter to fail to match
the player's GUID.
Vanilla CMSG_ITEM_QUERY_SINGLE has no GUID field (just uint32 entry),
causing servers to reject the oversized WotLK-format packets. Also fix
response parser: remove nonexistent statsCount field and use 5 damage
types instead of 2 to match Vanilla protocol.
Rebuild creature display lookups after expansion-specific DBC layout loads
(was using WotLK defaults before turtle layout was available). Add full
drag-and-drop support for bag items with server-side CMSG_SWAP_ITEM packets.
Add Classic-specific SMSG_ITEM_QUERY_SINGLE_RESPONSE parser for Vanilla
format differences (fewer damage types, no scaling stats, no Flags2).
Only show "Your home has been set" when the player actively changes their
bind point (via innkeeper), not on the initial login sync from the server.
Add "Local" to the auto-join channel list for Turtle WoW compatibility,
with a settings checkbox and persistence.
Add 27 new opcodes, packet builders/parsers, handler methods, inventory
extension with 28 bank slots + 7 bank bags, and UI windows for personal
bank, guild bank (6 tabs x 98 slots), and auction house (browse/sell/bid).
Fix Classic gossip parser to omit boxMoney/boxText fields not present in
Vanilla protocol, fix gossip icon labels with text-based NPC type detection,
and add Turtle WoW opcode mappings for bank and auction interactions.
Guild: add disband, leader transfer, public/officer note commands with
roster context menu showing rank names and officer notes column. Auto-refresh
roster after guild events.
Channels: fix city/region channels not working by accepting SMSG_CHANNEL_NOTIFY
during ENTERING_WORLD state (server auto-joins before VERIFY_WORLD) and handling
PLAYER_ALREADY_MEMBER notification.
Whisper: /r now switches to whisper tab and sets target to last sender,
matching WoW behavior.
Camera: extend WMO collision raycasting to work outside WMOs too.
M2 models like OrgrimmarFloatingEmbers and OrgrimmarSmokeEmitter have a
simple box mesh (24 verts, 36 indices) meant only to define particle
emitter bounds. Their blendMode was 0 (opaque), causing them to render
as large grey boxes. Detect these by checking for box geometry with
particle emitters and large bounds (>5 units), then mark as invisible.
Also add ANTIPORTAL and batch-disable flag checks to WMO group filtering.
Fix mailbox right-click (transposed CMSG_GAMEOBJECT_USE opcode, missing
mail opcodes in Turtle WoW JSON, decorative GO type filtering). Add
expansion-aware mail packet handling via PacketParsers: Classic format
(single item, no msgSize prefix, Vanilla field order) vs WotLK format
(attachment arrays, enchant slots). Fix CMSG_MAIL_TAKE_ITEM and
CMSG_MAIL_DELETE for Vanilla (no trailing fields). Add pulsing "New
Mail" indicator below minimap, SMSG_RECEIVED_MAIL and
MSG_QUERY_NEXT_MAIL_TIME handlers, and async sender name backfill.
- Fix FBlock color keys from 3-byte BGR to 4-byte RGBA (CImVector) to
prevent garbled purple/red colors from byte misalignment
- Add circular soft-edge falloff in particle fragment shader (GL_POINTS
rendered as squares by default)
- Apply default gravity (4.0 spray, 1.5 mist) when M2 gravity is 0 since
bone animation from .anim files isn't loaded yet
- Add drift velocity to speed=0 emitters so particles spread as mist
instead of clustering at static bone positions
- Run particle updates for all nearby instances, not just those in
boneWorkIndices_, to prevent particles freezing when bone culled
- Wrap animation time for particle models to keep emission tracks looping
- Cap particle scale to 1.5 and reduce point size multiplier (800→400)
- Desaturate FBlock colors 70% toward white for natural water appearance
- Reduce additive blend alpha to 5% and volume particles to 2%