Commit graph

985 commits

Author SHA1 Message Date
Kelsi
bf8710d6a4 feat: add Shift+V toggle for friendly player nameplates
V key now toggles enemy/NPC nameplates, while Shift+V independently
toggles friendly player nameplates. Setting is persisted to config.
2026-03-18 11:43:39 -07:00
Kelsi
9368c8a715 feat: add confirmation dialog before spending talent points
Clicking a learnable talent now opens a modal confirmation popup
showing the spell name and rank, preventing accidental talent spending.
2026-03-18 11:23:35 -07:00
Kelsi
d4c7157208 feat: add vendor purchase confirmation for expensive items
Shows a confirmation dialog before buying items costing 1 gold or more,
preventing accidental purchases. Displays item name, quantity, and
total cost in gold/silver/copper.
2026-03-18 11:16:43 -07:00
Kelsi
9b32a328c3 feat: add item stack splitting via Shift+right-click
Implements CMSG_SPLIT_ITEM (0x10E) with a slider popup for choosing
split count. Auto-finds empty destination slot across backpack and bags.
Shift+right-click on stackable items (count > 1) opens split dialog;
non-stackable items still get the destroy confirmation.
2026-03-18 11:07:27 -07:00
Kelsi
2dc5b21341 feat: add screenshot capture (PrintScreen key and /screenshot command)
Captures the Vulkan swapchain image to PNG via stb_image_write.
Screenshots saved to ~/.wowee/screenshots/ with timestamped filenames.
Cross-platform: BGRA→RGBA swizzle, localtime_r/localtime_s.
2026-03-18 10:47:34 -07:00
Kelsi
c8f80339f1 feat: display creature type on target frame
Show creature classification (Beast, Humanoid, Demon, etc.) next to the
level on the target frame. Useful for knowing which CC abilities apply
(Polymorph → Humanoid/Beast, Banish → Demon/Elemental, etc.).
2026-03-18 10:12:03 -07:00
Kelsi
209f60031e feat: respect loot roll voteMask for button visibility
Store the voteMask from SMSG_LOOT_START_ROLL and use it to conditionally
show Need/Greed/Disenchant/Pass buttons. Previously all four buttons were
always shown regardless of the server's allowed roll types.
2026-03-18 10:01:53 -07:00
Kelsi
63b4394e3e feat: world-space floating combat text above entities
Combat text (damage, heals, misses, crits, etc.) now floats above the
target entity in 3D space instead of appearing at fixed screen positions.
Text rises upward from the entity's head, with random horizontal stagger
to prevent stacking. HUD-only types (XP, Honor, Procs) and entries
without a valid entity anchor fall back to the original screen overlay.
2026-03-18 09:54:52 -07:00
Kelsi
e572cdfb4a feat: show guild names on player nameplates
Read PLAYER_GUILDID from entity update fields (UNIT_END + 3) and query
guild names via CMSG_GUILD_QUERY. Cache results in guildNameCache_ so
each guild ID is queried only once. Display <Guild Name> in grey below
the player name on nameplates. Fix handleGuildQueryResponse to not
overwrite the local player's guild data when querying other guilds.
2026-03-18 09:44:43 -07:00
Kelsi
1af5acba3f fix: show real player names on nameplates instead of "Player"
Player class declared its own 'name' member and getName()/setName()
that shadowed the inherited Unit::name. Since getName() is non-virtual,
code using Unit* pointers (nameplates, target frame, entity list) read
Unit::name (always empty) while Player::setName() wrote to the shadowed
Player::name. Removed the redundant declaration so Player inherits
name storage from Unit.
2026-03-18 08:49:16 -07:00
Kelsi
18c06d98ac fix: stop creature run animation when movement interpolation completes
Creatures were stuck in Run/Walk animation during the dead-reckoning
overrun window (up to 2x movement duration). The animation check used
isEntityMoving() which stays true through dead reckoning, causing
creatures to "run in place" after reaching their destination.

Add isActivelyMoving() which is true only during the active
interpolation phase (moveElapsed < moveDuration), and use it for
animation state transitions. Dead reckoning still works for position
extrapolation — only the animation now correctly stops at arrival.
2026-03-18 08:22:50 -07:00
Kelsi
eca570140a fix: eliminate 8-second teleport freeze on same-map teleport
Replace processAllReadyTiles() with bounded processReadyTiles() in the
same-map teleport and reconnect paths. processAllReadyTiles finalizes
every pending tile synchronously with a GPU sync wait, which caused
8+ second main-thread stalls when many tiles were queued. The bounded
version processes 1-4 tiles per call with async GPU upload — remaining
tiles finalize incrementally over subsequent frames.
2026-03-18 07:54:05 -07:00
Kelsi
f78d885e13 fix: add 60-second grace period to M2 model cleanup
Models that lose all instances are no longer immediately evicted from
GPU memory. Instead they get a 60-second grace period, preventing the
thrash cycle where GO models (barrels, chests, herbs) were evicted
every 5 seconds and re-loaded when the same object type respawned.
2026-03-18 07:00:50 -07:00
Kelsi
25138b5648 fix: use CMSG_OPEN_ITEM for locked containers (lockboxes)
Right-clicking a locked container (e.g. Dead-Tooth's Strong Box) was
sending CMSG_USE_ITEM with spellId=0, which the server rejects. Locked
containers (itemClass==1, inventoryType==0) now send CMSG_OPEN_ITEM
instead, letting the server auto-check the keyring for the required key.
2026-03-18 06:06:29 -07:00
Kelsi
2fb7901cca feat: enable water refraction by default
The VK_ERROR_DEVICE_LOST crash on AMD/Mali GPUs (barrier srcAccessMask)
was fixed in 2026-03-18. Enable refraction for new sessions so players
get the improved water visuals without needing to touch Settings.
Existing saved configs that explicitly disabled it are preserved.
2026-03-18 05:44:59 -07:00
Kelsi
e7fe35c1f9 feat: add right-click pet spell autocast toggle via CMSG_PET_SPELL_AUTOCAST
Right-clicking a castable pet ability (actionId > 6) in the pet action
bar now sends CMSG_PET_SPELL_AUTOCAST to toggle the spell's autocast
state. The local petAutocastSpells_ set is updated optimistically and
the tooltip shows the current state with a right-click hint.
2026-03-18 05:08:10 -07:00
Kelsi
5f3bc79653 feat: show queued spell icon in cast bar and expose getQueuedSpellId()
When a spell is queued in the 400ms window before the current cast ends,
render its icon dimmed (0.8 alpha) to the right of the cast bar progress,
with a "Queued: <name>" tooltip. The progress bar shrinks to accommodate
the icon when one is present.

Also exposes getQueuedSpellId() as a public const accessor on GameHandler
so the UI can observe the spell queue state without friend access.
2026-03-18 04:34:36 -07:00
Kelsi
277a26b351 feat: flash action bar button red when spell cast fails
Add SpellCastFailedCallback to GameHandler, fired from SMSG_CAST_RESULT
when result != 0. GameScreen registers the callback and records each failed
spellId in actionFlashEndTimes_ (keyed by spell ID, value = expiry time).

During action bar rendering, if a slot's spell has an active flash entry,
an AddRectFilled overlay is drawn over the button with alpha proportional
to remaining time (1.0→0.0 over 0.5 s), giving the same error-red flash
visual feedback as the original WoW client.
2026-03-18 04:30:33 -07:00
Kelsi
c1765b6b39 fix: defer loot item notification until item name is known from server query
When SMSG_ITEM_PUSH_RESULT arrives for an item not yet in the cache, store
a PendingItemPushNotif and fire the 'Received: [item]' chat message only
after SMSG_ITEM_QUERY_SINGLE_RESPONSE resolves the name and quality, so the
notification always shows a proper item link instead of 'item #12345'.

Notifications that are already cached emit immediately as before; multiple
pending notifs for the same item are all flushed on the single response.
2026-03-18 04:25:37 -07:00
Kelsi
09b0bea981 feat: add /stopmacro support and low durability warning for equipped items
- /stopmacro [conditions] halts remaining macro commands; supports all existing
  macro conditionals ([combat], [nocombat], [mod:shift], etc.) via the sentinel
  action trick on evaluateMacroConditionals
- macroStopped_ flag in GameScreen; executeMacroText resets and checks it after
  each command so /stopmacro mid-macro skips all subsequent lines
- Emit a "X is about to break!" UI error + system chat when an equipped item's
  durability drops below 20% via SMSG_UPDATE_OBJECT field delta; warning fires
  once per threshold crossing (prevDur >= maxDur/5, newDur < maxDur/5)
2026-03-18 04:14:44 -07:00
Kelsi
d7c377292e feat: show socket gems and consolidate enchant name DBC cache in item tooltips
Extends OnlineItemInfo to track gem enchant IDs (socket slots 2-4) from item
update fields; socket display now shows inserted gem name inline (e.g.
"Red Socket: Bold Scarlet Ruby"). Consolidates redundant SpellItemEnchantment
DBC loads into one shared static per tooltip variant.
2026-03-18 04:04:23 -07:00
Kelsi
1fd3d5fdc8 feat: display permanent and temporary enchants in item tooltips for equipped items
Tracks ITEM_ENCHANTMENT_SLOT 0 (permanent) and 1 (temporary) from item update
fields in OnlineItemInfo, then looks up names from SpellItemEnchantment.dbc and
renders them in both ItemDef and ItemQueryResponseData tooltip variants.
2026-03-18 03:50:24 -07:00
Kelsi
7967bfdcb1 feat: implement [target=mouseover] macro conditional via nameplate/raid hover
- Adds mouseoverGuid_ to GameHandler (set/cleared each frame by UI)
- renderNameplates() sets mouseoverGuid when the cursor is inside a
  nameplate's hit region; resets to 0 at frame start
- Raid frame cells set mouseoverGuid while hovered (IsItemHovered)
- evaluateMacroConditionals() resolves @mouseover / target=mouseover to
  the hover GUID; returns false (skip alternative) when no unit is hovered

This enables common healer macros like:
  /cast [target=mouseover,help,nodead] Renew; Renew
2026-03-18 03:09:43 -07:00
Kelsi
30513d0f06 feat: implement WoW macro conditional evaluator for /cast
Adds evaluateMacroConditionals() which parses the [cond1,cond2] Spell;
[cond3] Spell2; Default syntax and returns the first matching
alternative. Supported conditions:

- mod:shift/ctrl/alt, nomod  — keyboard modifier state
- target=player/focus/target, @player/@focus/@target — target override
- help / harm (noharm / nohelp)  — target faction check
- dead / nodead                  — target health check
- exists / noexists              — target presence check
- combat / nocombat              — player combat state
- noform / nostance / form:0     — shapeshift/stance state
- Unknown conditions are permissive (true) to avoid false negatives.

/cast now resolves conditionals before spell lookup and routes
castSpell() to the [target=X] override GUID when specified.
isHostileFaction() exposed as isHostileFactionPublic() for UI use.
2026-03-18 03:04:45 -07:00
Kelsi
ed3bca3d17 fix: escape newlines in macro cfg persistence; execute all macro lines
- Macro text is now escaped (\\n, \\\\) on save and unescaped on load,
  fixing multiline macros silently truncating after the first line in
  the character config file.
- executeMacroText() runs every non-comment line of a macro body in
  sequence (WoW behaviour), replacing the firstMacroCommand() approach
  that only fired the first actionable line. The server still enforces
  one spell-cast per click; non-cast commands (target, equip, pet, etc.)
  now all execute correctly in the same macro activation.
2026-03-18 02:44:28 -07:00
Kelsi
2c86fb4fa6 feat: implement client-side macro text storage and execution
Macros in WoW are client-side — the server sends only a macro index via
SMSG_ACTION_BUTTONS, never the text. This commit adds local storage and
a UI so macro slots are actually usable.

- GameHandler: getMacroText/setMacroText accessors backed by macros_ map;
  text is persisted to the character .cfg file as macro_N_text= entries
- Action bar left-click: MACRO slot executes first line of macro text as
  a chat/slash command (same path as /cast, /use, etc.)
- Context menu: "Execute" and "Edit" items for MACRO slots; "Edit" opens
  a multiline modal editor (320×80 px, up to 255 chars) with Save/Cancel
- Tooltip: shows macro text body below the index; hints "right-click to
  Edit" when no text is set yet
2026-03-18 02:07:59 -07:00
Kelsi
4907f4124b feat: implement spell queue window (400ms pre-cast)
When castSpell() is called while a timed cast is in progress and
castTimeRemaining <= 0.4s, store the spell in queuedSpellId_ instead
of silently dropping it.  handleSpellGo() fires the queued spell
immediately after clearing the cast state, matching the ~400ms spell
queue window in Blizzlike WoW clients.

Queue is cleared on all cancel/interrupt paths: cancelCast(),
handleCastFailed(), SMSG_CAST_RESULT failure, SMSG_SPELL_FAILED,
world-teardown, and worldport ACK.  Channeled casts never queue
(cancelling a channel should remain explicit).
2026-03-18 00:21:46 -07:00
Kelsi
5a5c2dcda3 feat: implement self-resurrection (Reincarnation/Twisting Nether)
SMSG_PRE_RESURRECT was silently discarded; Shamans with Reincarnation
and Warlocks with Twisting Nether could never see or use the self-res
ability. Now:

- SMSG_PRE_RESURRECT sets selfResAvailable_ flag when addressed to the
  local player
- Death dialog gains a "Use Self-Resurrection" button (blue, shown above
  Release Spirit) when the flag is set
- Clicking it sends CMSG_SELF_RES (empty body) and clears the flag
- selfResAvailable_ is cleared on all resurrection and session-reset
  paths so it never bleeds across deaths or logins
2026-03-18 00:06:39 -07:00
Kelsi
b0046fa777 feat: track PvP corpse-reclaim delay and show countdown in UI
SMSG_CORPSE_RECLAIM_DELAY is now stored as an absolute expiry timestamp
(steady_clock ms) instead of being discarded after a chat message.

GameHandler::getCorpseReclaimDelaySec() returns remaining seconds (0 when
reclaim is available). The "Resurrect from Corpse" button now:
- Disables and shows the remaining seconds when a PvP delay is active
- Shows the usual "Corpse: N yards" helper text when available
Also resets corpseReclaimAvailableMs_ on world/session teardown.
2026-03-17 23:52:45 -07:00
Kelsi
d99fe8de0f feat: add Sort Bags button to backpack window
Adds Inventory::sortBags() which collects all items from the backpack
and equip bags, sorts them client-side by quality descending → item ID
ascending → stack count descending, then writes them back. A "Sort Bags"
SmallButton is rendered in the backpack footer with a tooltip explaining
the sort order.

The sort is purely local (no server packets) since the WoW protocol has
no sort-bags opcode; it provides an instant, session-persistent visual
reorder.
2026-03-17 23:29:50 -07:00
Kelsi
6f936f258f fix: consume all SpellCastTargets bytes in WotLK SpellGoParser
Applied the same SpellCastTargets fix from SpellStartParser (dd64724)
to SpellGoParser: after parsing hit/miss target lists, now reads the
full target section (UNIT/UNIT_MINIPET/CORPSE/GAMEOBJECT packed GUID,
ITEM/TRADE_ITEM packed GUID, SOURCE/DEST PackedGuid+3floats, null-
terminated STRING). Also adds targetGuid field to SpellGoData so
callers can read the primary target. Prevents stream misalignment on
ground-targeted AoE spells (e.g. Blizzard, Rain of Fire).
2026-03-17 22:26:05 -07:00
Kelsi
32497552d1 fix: R key resets camera angles only; consume all SpellCastTargets bytes
- CameraController::resetAngles(): new method that only resets yaw/pitch
  without teleporting the player. R key now calls resetAngles() instead
  of reset() so pressing R no longer moves the character to spawn.
  The full reset() (position + angles) is still used on world-entry and
  respawn via application.cpp.

- packet_parsers_classic: parseSpellStart now calls
  skipClassicSpellCastTargets() to consume all target payload bytes
  (UNIT, ITEM, SOURCE_LOCATION, DEST_LOCATION, etc.) instead of only
  handling UNIT/OBJECT. Prevents packet-read corruption for ground-
  targeted AoE spells.

- packet_parsers_tbc: added skipTbcSpellCastTargets() static helper
  (uint32 targetFlags, full payload coverage including TRADE_ITEM and
  STRING targets). parseSpellStart now uses it.
2026-03-17 21:52:45 -07:00
Kelsi
a731223e47 fix: right-clicking a quest-starting item now opens the quest offer dialog
Items with startQuestId != 0 were calling useItemBySlot()/useItemInBag()
which sends CMSG_USE_ITEM — but quest-starting items have no on-use spell,
so the server silently ignored the packet and no quest dialog appeared.

Fix:
- offerQuestFromItem(itemGuid, questId): sends CMSG_QUESTGIVER_QUERY_QUEST
  with the item's own GUID as the questgiver GUID. The server responds with
  SMSG_QUESTGIVER_QUEST_DETAILS which handleQuestDetails() already picks up
  and opens the Accept/Decline dialog with full rewards/description.
- getBagItemGuid(bagIndex, slotIndex): resolves the per-slot item GUID from
  the bag's containerContents_ map (mirrors the logic inside useItemInBag).
- inventory_screen.cpp right-click handler: checks item.startQuestId != 0
  before the equip/use branch; if set, resolves item GUID and calls
  offerQuestFromItem. Works for both backpack slots and bag slots.
2026-03-17 21:38:08 -07:00
Kelsi
c70740fcdf feat: wire Warden funcList_ dispatchers and implement PacketHandler call
Previously initializeModule() read the 4 WardenFuncList function addresses
from emulated memory, logged them, then discarded them — funcList_ was never
populated, so tick(), generateRC4Keys(), and processCheckRequest() were
permanently no-ops even when the Unicorn emulator successfully ran the module.

Changes:
- initializeModule() now wraps each non-null emulated function address in a
  std::function lambda that marshals args to/from emulated memory via
  emulator_->writeData/callFunction/freeMemory
- generateRC4Keys: copies 4-byte seed to emulated space, calls function
- unload: calls function with NULL (module saves own RC4 state)
- tick: direct uint32_t(deltaMs) dispatch, returns emulated EAX
- packetHandler: 2-arg variant for generic callers
- Stores emulatedPacketHandlerAddr_ for full 4-arg call in processCheckRequest
- processCheckRequest() now calls the emulated PacketHandler with the proper
  4-argument stdcall convention: (data, size, responseOut, responseSizeOut),
  reads back the response size and bytes, returns them in responseOut
- unload() resets emulatedPacketHandlerAddr_ to 0 for clean re-initialization
- Remove dead no-op renderObjectiveTracker() (no call sites, superseded)
2026-03-17 21:29:09 -07:00
Kelsi
005b1fcb54 feat: implement Warden API stub dispatch via Unicorn UC_HOOK_CODE
Previously hookAPI() allocated a stub address and registered a C++ handler
but never stored the handler or wrote any executable code to the stub
region, meaning any Warden module call to a Windows API would execute zeros
and crash or silently return garbage.

Changes:
- Store ApiHookEntry {argCount, handler} per stub address in apiHandlers_
- Write RET (0xC3) to stub memory as a safe fallback
- Register UC_HOOK_CODE over the API stub address range during initialize()
- hookCode() now detects stub addresses, reads args from the emulated stack,
  dispatches to the C++ handler, then simulates stdcall epilogue by setting
  EAX/ESP/EIP so Unicorn returns cleanly to the caller
- Convert static-local nextStubAddr to instance member nextApiStubAddr_
  so re-initialization resets the allocator correctly
- Known arg counts for all 7 registered Windows APIs (VirtualAlloc,
  VirtualFree, GetTickCount, Sleep, GetCurrentThreadId,
  GetCurrentProcessId, ReadProcessMemory)
2026-03-17 21:22:41 -07:00
Kelsi
49ba89dfc3 feat: handle SMSG_PET_UNLEARN_CONFIRM with pet talent respec dialog
Parses the pet talent wipe confirm packet (petGuid + cost), shows a
confirmation dialog matching the player talent reset UX, and sends
CMSG_PET_UNLEARN_TALENTS on confirmation. Completes the pet talent
respec flow for Hunters/Warlocks on WotLK servers.
2026-03-17 21:13:27 -07:00
Kelsi
67c8101f67 fix: add missing TOGGLE_SKILLS to keybinding_manager (fixes CI build failure) 2026-03-17 21:08:02 -07:00
Kelsi
5df5f4d423 feat: handle SMSG_PET_RENAMEABLE to auto-open pet rename dialog on first tame
When the server sends SMSG_PET_RENAMEABLE (after taming a pet for the first
time), the pet rename modal now automatically opens so the player can name
their new pet without needing to right-click the pet frame.
2026-03-17 20:59:29 -07:00
Kelsi
113be66314 feat: parse MSG_BATTLEGROUND_PLAYER_POSITIONS and show flag carriers on minimap
Replaces the silent consume with full packet parsing: reads two lists of
(guid, x, y) positions (typically ally and horde flag carriers) and stores
them in bgPlayerPositions_. Renders each as a colored diamond on the minimap
(blue=group0, red=group1) with a "Flag carrier" tooltip showing the player's
name when available.
2026-03-17 20:54:59 -07:00
Kelsi
48cb7df4b4 feat: add Skills/Professions window (K key) with per-category progress bars
Implements renderSkillsWindow() showing all player skills grouped by
DBC category (Professions, Secondary Skills, Class Skills, Weapon Skills,
Armor, Languages) with value/max progress bars and a bonus breakdown tooltip.
Hooked up to the TOGGLE_SKILLS keybinding (K by default).
2026-03-17 20:46:41 -07:00
Kelsi
d44d462686 feat: add auto-repair at vendor open
When 'Auto Repair' is enabled in Settings > Gameplay, all damaged
equipment is automatically repaired when opening any armorer vendor
(canRepair=true). The repair is skipped when no items are actually
damaged to avoid a pointless server round-trip. A system chat message
confirms the repair. Setting persists to ~/.wowee/settings.cfg as
auto_repair.
2026-03-17 20:27:45 -07:00
Kelsi
072f256af6 feat: add auto-sell grey items on vendor open
When 'Auto Sell Greys' is enabled in Settings > Gameplay, all grey
(ItemQuality::POOR) items in the backpack and extra bags are sold
automatically when opening a vendor window. Items with no sell price
are skipped. A system chat message reports the number of items sold
and total gold received. The setting persists to ~/.wowee/settings.cfg
under the key auto_sell_grey.
2026-03-17 20:21:06 -07:00
Kelsi
4ce6fdb5f3 feat: color player cast bar by spell school from Spell.dbc
The player's own cast bar now uses spell-school-based colors for quick
identification: Fire=orange-red, Frost=icy blue, Shadow=purple,
Arcane=violet, Nature=green, Holy=golden, Physical=gold. Channels
remain blue regardless of school. Adds getSpellSchoolMask() using the
already-loaded Spell.dbc cache (schoolMask field, covering all
expansions including Classic SchoolEnum→bitmask conversion).
2026-03-17 19:56:52 -07:00
Kelsi
d0df6eed2c feat: show corpse skull marker on world map when player is a ghost
When the player dies and releases spirit, the world map now renders a
bone-white X cross at the corpse's location (matching the existing
minimap skull marker). The marker appears only when the player is a
ghost with an unclaimed corpse on the same map, and shows a "Your
corpse" tooltip on hover. Implemented via setCorpsePos() on WorldMap,
called from renderWorldMap() using getCorpseCanonicalPos().
2026-03-17 19:52:17 -07:00
Kelsi
279b4de09a feat: color cast bars green/red by spell interruptibility from Spell.dbc
Load AttributesEx from Spell.dbc for all expansions (Classic/TBC/WotLK/
Turtle). Check SPELL_ATTR_EX_NOT_INTERRUPTIBLE (bit 4 = 0x10) to classify
each cast as interruptible or not when SMSG_SPELL_START arrives.

Target frame and nameplate cast bars now use:
- Green: spell can be interrupted by Kick/Counterspell/Pummel etc.
- Red: spell is immune to interrupt (boss abilities, instant-cast effects)
Both colors pulse faster at >80% completion to signal the closing window.

Adds GameHandler::isSpellInterruptible() and UnitCastState::interruptible.
2026-03-17 19:43:19 -07:00
Kelsi
f9947300da feat: show zone entry text on every zone crossing via SMSG_INIT_WORLD_STATES
Previously the "Entering: [Zone]" overlay only triggered when the terrain
renderer loaded a new map. Now it also fires whenever worldStateZoneId_
changes (sent by the server via SMSG_INIT_WORLD_STATES on each zone
crossing), giving correct "Entering: Ironforge", "Entering: Wailing
Caverns" etc. display for sub-zones and dungeon entries without requiring
a full map reload.

- Added lastKnownWorldStateZoneId_ to track server-reported zone changes
- renderZoneText() now takes GameHandler& to access getWorldStateZoneId()
  and getWhoAreaName() for name lookup via WorldMapArea.dbc cache
- Renderer zone name still checked as a fallback for map-level transitions
- Both sources de-duplicate to avoid triggering the same text twice
2026-03-17 19:14:17 -07:00
Kelsi
d60d296b77 feat: show discovered taxi nodes as markers on the world map
Add gold diamond markers for every flight master the player has already
discovered (knownTaxiMask_), read from TaxiNodes.dbc and filtered to the
current continent/map being displayed:
- WorldMapTaxiNode struct carries canonical WoW coords + known flag
- WorldMap::setTaxiNodes() accepts the per-frame list from game_screen
- renderImGuiOverlay() projects each known node to UV, draws a gold
  diamond (AddQuadFilled) with a dark outline, and shows the node name
  as a tooltip on hover
- GameHandler::isKnownTaxiNode(id) checks knownTaxiMask_[] efficiently
- Markers update live — newly discovered nodes appear without reopening
  the map
2026-03-17 19:01:03 -07:00
Kelsi
488ec945b6 feat: display glancing and crushing blows in combat text and log
Add GLANCING (hitInfo 0x800) and CRUSHING (hitInfo 0x1000) as distinct
combat text types so players see mechanics feedback they expect from
Classic/TBC content:
- Glancing: shown as "~{amount}" in muted yellow/red; "glances for N" in
  the combat log
- Crushing: shown as "{amount}!" in bright orange/red; "crushes for N!"
  in the combat log
Both types are counted toward DPS meter accumulation. AttackerStateUpdateData
gains isGlancing()/isCrushing() helpers alongside the existing isCrit()/isMiss().
2026-03-17 18:51:48 -07:00
Kelsi
36fed15d43 feat: separate cast/impact kit paths in spell visual DBC lookup
loadSpellVisualDbc() now builds two distinct maps:
  spellVisualCastPath_  — visualId → M2 via SpellVisual.CastKit chain
  spellVisualImpactPath_ — visualId → M2 via SpellVisual.ImpactKit chain

playSpellVisual() accepts useImpactKit=false (default, cast) / true (impact).
SMSG_PLAY_SPELL_IMPACT passes useImpactKit=true so impact effects (explosions,
debuff indicators) use the ImpactKit model instead of the CastKit model.
Added ImpactKit field to all four dbc_layouts.json files.
2026-03-17 18:30:11 -07:00
Kelsi
315adfbe93 feat: implement SMSG_PLAY_SPELL_VISUAL with SpellVisual DBC chain lookup
Parse SMSG_PLAY_SPELL_VISUAL (casterGuid + visualId) and spawn a
transient M2 spell effect at the caster's world position.

DBC chain: SpellVisual.dbc → SpellVisualKit.dbc → SpellVisualEffectName.dbc
Lookup priority: CastKit.SpecialEffect0, fallback to MissileModel.
Models are lazy-loaded and cached by path; instances auto-expire after 3.5s.
DBC layouts added to all four expansion layout files (Classic/TBC/WotLK/Turtle).
2026-03-17 18:23:05 -07:00