Commit graph

1722 commits

Author SHA1 Message Date
Kelsi
28550dbc99 fix: reduce GO fallback hit radius to prevent invisible chair click-lock
Fallback sphere for GameObjects without a loaded renderer instance was 2.5f,
causing invisible/unloaded chairs in Goldshire Inn to be accidentally targeted
during camera right-drag. This sent CMSG_GAMEOBJ_USE which set server stand
state to SIT, trapping the player until a stand-up packet was sent.

Reduce fallback radius to 1.2f and height offset to 1.0f so only deliberate
close-range direct clicks register on unloaded GO geometry.
2026-03-10 19:59:23 -07:00
Kelsi
564a286282 fix: stand-up-on-move and nameplate position tracking
Camera controller / sitting:
- Any movement key (WASD/QE/Space) pressed while sitting now clears the
  sitting flag immediately, matching WoW's sit-to-stand-on-move behaviour
- Added StandUpCallback: when the player stands up via local input the
  callback fires setStandState(0) → CMSG_STAND_STATE_CHANGE(STAND) so
  the server releases the sit lock and restores normal movement
- Fixes character getting stuck in sit state after accidentally
  right-clicking a chair GO in Goldshire Inn (or similar)

Nameplates:
- Use getRenderPositionForGuid() (renderer visual position) as primary
  source for nameplate anchor, falling back to entity X/Y/Z only when
  no render instance exists yet; keeps health bars in sync with the
  rendered model instead of the parallel entity interpolator
2026-03-10 19:49:33 -07:00
Kelsi
48d15fc653 fix: quest markers, level-up effect, and emote loop
- renderer: construct QuestMarkerRenderer via make_unique (was never
  instantiated, causing getQuestMarkerRenderer() to always return null
  and all quest-marker updates to be silently skipped)
- m2_renderer: add "levelup" to effectByName so LevelUp.m2 is treated
  as a spell effect (additive blend, no collision, particle-dominated)
- renderer: auto-cancel non-looping emote animations when they reach
  end-of-sequence, transitioning player back to IDLE state
2026-03-10 19:41:01 -07:00
Kelsi
34bab8edd6 feat: use rich spell tooltip for buff/debuff frame icons
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Player buff bar and target debuff bar icons now show full spell tooltip
(school, cost, cast time, range, description) on hover, matching the
action bar and spellbook. Falls back to plain spell name if DBC is not
loaded. Remaining aura duration is shown below the spell body.
2026-03-10 19:33:25 -07:00
Kelsi
caf0d18393 feat: show rich spell tooltip on action bar hover
Expose SpellbookScreen::renderSpellInfoTooltip() as a public method,
then use it in the action bar slot tooltip. Action bar spell tooltips
now show the same full tooltip as the spellbook: spell school (colored),
mana/rage/energy cost, cast time, range, cooldown, and description.

Falls back to a plain spell name if DBC data is not yet loaded.
Hearthstone location note is appended after the rich body.
Cooldown text moved inside each branch for consistent styling.
2026-03-10 19:31:46 -07:00
Kelsi
7bbf2c7769 refactor: improve quest offer reward item display
- Use getQualityColor() for consistent quality coloring (choice+fixed)
- Show item icons for fixed rewards (previously text-only)
- Replace useless "Reward option" tooltip with real item name+description
- Render icon before selectable label (not after) for choice rewards
- Call ensureItemInfo for all reward items to trigger async fetch
- Use structured bindings (C++17) to unify icon+color resolution
2026-03-10 19:25:26 -07:00
Kelsi
bae3477c94 feat: display spell school in spellbook tooltip
Load SchoolMask (TBC/WotLK bitmask) or SchoolEnum (Classic/Turtle 0-6
enum, converted to mask via 1<<N) from Spell.dbc into SpellInfo.

renderSpellTooltip now shows the spell school name (Holy/Fire/Nature/
Frost/Shadow/Arcane) in the appropriate school color between the spell
rank and resource cost. Physical school is suppressed as it is the
implied default. Multi-school spells display both names separated by /.

WotLK DBC fallback path uses field 225 for SchoolMask.
2026-03-10 19:22:48 -07:00
Kelsi
458a95ae8e refactor: use quality colors and hover tooltips for quest reward items
Replace flat white coloring with item quality colors and add hover tooltips
showing item name (quality-colored) and description for quest acceptance window.
Extract renderQuestRewardItem lambda to eliminate code duplication between
choice and fixed reward item rendering.
2026-03-10 19:12:43 -07:00
Kelsi
1ff48259cc feat: display quest reward items in quest details acceptance window
Parse and store reward items (choice and fixed) from SMSG_QUESTGIVER_QUEST_DETAILS
in both WotLK (QuestDetailsParser) and TBC/Classic (TbcPacketParsers) parsers.
Show item icons, names, and counts in the quest acceptance dialog alongside XP/money.
Move QuestRewardItem before QuestDetailsData in header to fix forward-reference.
2026-03-10 19:05:34 -07:00
Kelsi
9f8a0907c4 feat: add spell stats to TBC and Turtle WoW DBC layouts
TBC 2.4.3: TBC added 7 fields after position 5 vs Classic 1.12, giving
a consistent +7 offset for all fields in the middle/upper range. Derive
CastingTimeIndex (22), PowerType (35), ManaCost (36), and RangeIndex (40)
from the verified Classic positions (15/28/29/33) using this offset.
This enables mana cost, cast time, and range display in the TBC spellbook.

Turtle WoW: Inherits Classic 1.12.1 Spell.dbc field layout. Add
CastingTimeIndex (15), PowerType (28), ManaCost (29), RangeIndex (33),
and SpellRange.MaxRange (2) matching Classic 1.12. Enables spell stat
display for Turtle WoW players.

Also update README: pet action bar (10 slots, icons, autocast tinting).
2026-03-10 18:55:01 -07:00
Kelsi
373dbbf95d fix: use authoritative autocast state for pet action bar and correct tooltip labels
- Use isPetSpellAutocast() instead of parsing the slot value high byte for
  autocast detection; the authoritative source is the SMSG_PET_SPELLS spell
  list activeFlags, not the action bar slot value.
- Fix tooltip mapping: actionId==2 maps to "Follow", actionId==5 to "Attack",
  others to "Stay" (removed erroneous duplicate Follow case for actionId==4).
- Update spellbook comment: TBC Spell.dbc has ~220+ fields (not ~167).
2026-03-10 18:37:05 -07:00
Kelsi
2e38a9af65 feat: add pet action bar to pet frame UI
Show the 10 SMSG_PET_SPELLS action slots as clickable icon/text buttons
in the pet frame. Spell slots with icons render as ImageButtons; built-in
commands (Attack/Follow/Stay) render as text buttons. Autocast-on slots
are tinted green. Clicking a spell slot sends CMSG_PET_ACTION with the
current target GUID; built-in commands send without a target. Tooltips
show the spell name on hover.
2026-03-10 18:26:02 -07:00
Kelsi
53d144c51e fix: expansion-aware SpellRange.dbc loading and Classic spell tooltip fields
SpellRange.dbc layout fix:
- Classic 1.12 uses field 2 (MaxRange), TBC/WotLK use field 4 (MaxRangeHostile)
- Add SpellRange layout to each expansion's dbc_layouts.json
- Replace hardcoded field 5 with layout-driven lookup in SpellRange loading
- Corrects previously wrong range values in WotLK spellbook tooltips

Classic 1.12 Spell.dbc field additions:
- Add CastingTimeIndex=15, PowerType=28, ManaCost=29, RangeIndex=33 to
  classic/dbc_layouts.json so Classic spellbook shows mana cost, cast time,
  and range in tooltips

Trainer fieldCount guard:
- Lower Trainer::loadSpellNameCache() Spell.dbc fieldCount threshold from
  154 to 148 so Classic trainers correctly resolve spell names from Spell.dbc
2026-03-10 18:09:21 -07:00
Kelsi
2f3f9f1a21 fix: enable Classic/Turtle Spell.dbc loading and add WotLK optional spell fields to layout
Lower fieldCount threshold from 154→148 so Classic 1.12 and Turtle WoW Spell.dbc
(148 fields, Tooltip at index 147) are accepted by the spellbook loader instead of
being silently skipped.

Add PowerType/ManaCost/CastingTimeIndex/RangeIndex to the WotLK dbc_layouts.json
Spell section so mana cost, cast time, and range continue to display correctly when
the DBC layout path is active (the old hardcoded-index fallback path is now bypassed
since layout-path loads spell names first and spellData.empty() is no longer true).
2026-03-10 17:53:17 -07:00
Kelsi
fcb133dbbe fix: guard optional DBC field reads against out-of-bounds indices for Classic/TBC
When expansion DBC layouts lack PowerType/ManaCost/CastingTimeIndex/RangeIndex,
default to UINT32_MAX instead of WotLK hardcoded indices to prevent reading wrong
data from Classic/TBC Spell.dbc files. tryLoad now skips any field index >= fieldCount.
2026-03-10 17:41:05 -07:00
Kelsi
63c09163dc fix: correct SMSG_ACTION_BUTTONS parsing for Classic and TBC expansions
Classic 1.12 sends 120 action button slots with no leading mode byte
(480 bytes total). TBC 2.4.3 sends 132 slots with no mode byte (528
bytes). WotLK 3.3.5a sends a uint8 mode byte followed by 144 slots
(577 bytes total).

The previous code always consumed a mode byte and assumed 144 slots.
On Classic servers this would misparse the first action button (reading
one byte as the mode, shifting all subsequent entries), causing the
action bar to load garbage spells/items from the server.

Fixed by detecting expansion type at runtime and selecting the
appropriate slot count and presence of mode byte accordingly.
2026-03-10 17:28:20 -07:00
Kelsi
56588e0dad fix: correct SMSG_SPELL_COOLDOWN parsing for Classic 1.12 expansion
Classic 1.12 sends guid(8) + N×[spellId(4)+itemId(4)+cooldown(4)] with
no flags byte and 12 bytes per entry, while TBC/WotLK send guid(8)+
flags(1) + N×[spellId(4)+cooldown(4)] with 8 bytes per entry.

The previous parser always consumed the WotLK flags byte, which on
Classic servers would corrupt the first spell ID (reading one byte
into spellId) and misalign all subsequent entries. Fixed by detecting
isClassicLikeExpansion() and using the correct 12-byte-per-entry
format (skipping itemId) for Classic builds.
2026-03-10 17:19:43 -07:00
Kelsi
59597ff39e feat: display mana cost, cast time, and range in spellbook tooltip
Load SpellCastTimes.dbc and SpellRange.dbc during DBC init and
populate SpellInfo.castTimeMs, manaCost, powerType, rangeIndex.
renderSpellTooltip now shows resource cost (Mana/Rage/Energy/Focus),
cast time ("Instant cast" or "X.X sec cast"), and range ("X yd range"
or "Melee range") for active spells, matching WoW's native tooltip
layout with cost on left and cast time aligned to the right.
2026-03-10 17:14:46 -07:00
Kelsi
068b6bc2cb fix: parse SMSG_INVENTORY_CHANGE_FAILURE additional fields correctly
Previously the handler read only the error byte, producing:
- A literal "%d" in the "requires level" message (error 1)
- No consumption of the following item GUIDs and bag slot bytes

Now reads item_guid1(8) + item_guid2(8) + bag_slot(1) after the error
byte, and for error 1 (EQUIP_ERR_LEVEL_REQ) reads the required level
uint32 and shows the correct message: "You must reach level N to use
that item."
2026-03-10 17:10:31 -07:00
Kelsi
62b7622f75 feat: parse and display StartQuest field from item query response
Items that begin a quest (like quest starter drop items) now show
"Begins a Quest" in the tooltip.

All three expansion parsers (WotLK/TBC/Classic) now read the
PageText/LanguageID/PageMaterial/StartQuest fields after Description.
startQuestId is propagated through all 5 inventory rebuild paths and
stored in ItemDef.
2026-03-10 17:05:04 -07:00
Kelsi
5fcf71e3ff feat: add stat diff comparison in item shift-tooltip
Shift-hover tooltip now shows stat differences vs the equipped item
instead of just listing the equipped item's stats. Each compared stat
shows: value (▲ gain green / ▼ loss red / unchanged grey).

Covers: DPS (weapons), Armor, Str/Agi/Sta/Int/Spi, and all extra stats
(Hit, Crit, Haste, Expertise, AP, SP, Resilience, MP5, etc.) using a
union of stat types from both items.
2026-03-10 17:03:11 -07:00
Kelsi
321aaeae54 feat: capture and display all item stat types in tooltips
Previously only the 5 primary stats (Str/Agi/Sta/Int/Spi) were stored,
discarding hit rating, crit, haste, attack power, spell power, resilience,
expertise, armor penetration, MP5, and many others.

Changes:
- Add ItemDef::ExtraStat and ItemQueryResponseData::ExtraStat arrays
- All three expansion parsers (WotLK/TBC/Classic) now capture non-primary
  stat type/value pairs into extraStats instead of silently dropping them
- All 5 rebuildOnlineInventory paths propagate extraStats to ItemDef
- Tooltip now renders each extra stat on its own line with a name lookup
  covering all common WotLK stat types (hit, crit, haste, AP, SP, etc.)
- Also fix Classic/TBC bag-content and bank-bag paths that were missing
  bindType, description propagation from previous commits
2026-03-10 17:00:24 -07:00
Kelsi
6075207d94 fix: complete TBC item query parsing for itemLevel, spells, binding, description
TBC parser was truncating item query response after armor/resistances,
discarding itemLevel, requiredLevel, spell slots, bind type, and description.

Now stores itemLevel/requiredLevel, reads AmmoType+RangedModRange, reads
5 spell slots into data.spells[], reads bindType and description cstring.
Matches the Classic and WotLK parser fixes from the previous commits.
2026-03-10 16:53:00 -07:00
Kelsi
5004929f07 fix: complete classic/vanilla item query parsing for itemLevel, spells, binding, description
The classic packet parser was stopping after armor/resistances/delay without
reading the remaining tail fields present in vanilla 1.12.1 item packets:
- Store itemLevel and requiredLevel (were read but discarded)
- Read AmmoType and RangedModRange after delay
- Read 5 spell slots (SpellId, SpellTrigger, Charges, Cooldown, Category, CatCooldown)
- Read Bonding type (bindType) after spells
- Read Description (flavor/lore text) cstring after bonding

All new fields now flow into ItemDef via rebuildOnlineInventory and display in
the item tooltip (same as WotLK/TBC — binding text, spell effects, description).
2026-03-10 16:50:14 -07:00
Kelsi
76bd6b409e feat: enhance item tooltips with binding, description, speed, and spell effects
- Parse Bonding and Description fields from SMSG_ITEM_QUERY_SINGLE_RESPONSE
  (read after the 5 spell slots: bindType uint32, then description cstring)
- Add bindType and description to ItemQueryResponseData and ItemDef
- Propagate bindType and description through all 5 rebuildOnlineInventory paths
- Tooltip now shows: "Binds when picked up/equipped/used/quest item"
- Tooltip now shows weapon damage range ("X - Y Damage") and speed ("Speed 2.60")
  on same line, plus DPS in parentheses below
- Tooltip now shows spell effects ("Use: <SpellName>", "Equip: <SpellName>",
  "Chance on Hit: ...") using existing getSpellName() lookup
- Tooltip now shows item flavor/lore description in italic-style yellow text
2026-03-10 16:47:55 -07:00
Kelsi
f53f16a59b feat: add ITEM_FIELD_DURABILITY/MAXDURABILITY to all expansion update_fields
- Classic/Turtle: indices 48/49 (no spell-charge fields between stack
  count and durability in 1.12)
- TBC: indices 60/61 (same layout as WotLK, matches TBC 2.4.3 item fields)
- WotLK: already added in previous commit

Enables durability tracking across all supported expansion profiles.
2026-03-10 16:31:18 -07:00
Kelsi
dce8a4e442 fix: propagate sellPrice to all rebuildOnlineInventory() inventory paths
Equipment, backpack, and bag-content paths were missing def.sellPrice
assignment — only bank/bank-bag paths had it. This caused the "Sell"
price in item tooltips to show 0g 0s 0c for equipped and backpack items.
2026-03-10 16:30:01 -07:00
Kelsi
30058a8df5 fix: preserve vendor canRepair flag when SMSG_LIST_INVENTORY arrives
ListInventoryParser::parse() overwrites currentVendorItems entirely,
resetting canRepair=false. Save the flag before parsing and restore it
after so the "Repair All" button remains visible when an armorer vendor
also sells items.
2026-03-10 16:28:59 -07:00
Kelsi
1793549550 feat: parse and display item level and required level in tooltips
- Add itemLevel/requiredLevel fields to ItemQueryResponseData (parsed
  from SMSG_ITEM_QUERY_SINGLE_RESPONSE) and ItemDef
- Propagate through all 5 rebuildOnlineInventory() paths
- Show "Item Level N" and "Requires Level N" in item tooltip in
  standard WoW order (below item name, above required level/stats)
2026-03-10 16:26:20 -07:00
Kelsi
67db7383ad feat: add durability bar overlay on equipment slots in character panel
Draw a 3px color-coded strip at the bottom of each equipment slot icon
(green >50%, yellow >25%, red <=25%) so broken or near-broken gear is
immediately visible at a glance without opening the tooltip.
2026-03-10 16:23:12 -07:00
Kelsi
0afa41e908 feat: implement item durability tracking and vendor repair
- Add ITEM_FIELD_DURABILITY (60) and ITEM_FIELD_MAXDURABILITY (61) to
  update_field_table.hpp enum and wotlk/update_fields.json
- Add curDurability/maxDurability to OnlineItemInfo and ItemDef structs
- Parse durability fields in OBJECT_CREATE and OBJECT_VALUES handlers;
  preserve existing values on partial updates (fixes stale durability
  being reset to 0 on stack-count-only updates)
- Propagate durability to ItemDef in all 5 rebuildOnlineInventory() paths
- Implement GameHandler::repairItem() and repairAll() via CMSG_REPAIR_ITEM
  (itemGuid=0 repairs all equipped items per WotLK protocol)
- Add canRepair flag to ListInventoryData; set it when player selects
  GOSSIP_OPTION_ARMORER in gossip window
- Show "Repair All" button in vendor window header when canRepair=true
- Display color-coded durability in item tooltip (green >50%, yellow
  >25%, red <=25%)
2026-03-10 16:21:09 -07:00
Kelsi
094ef88e57 fix: NPC animation/position sync for distant creatures and restore WoW ding overlay
- application.cpp creature sync loop: use entity->isEntityMoving() alongside
  planarDist to detect movement; entities > 150u have stale getX/Y/Z (distance
  culled in GameHandler::update) but isEntityMoving() correctly reflects active
  startMoveTo paths from SMSG_MONSTER_MOVE. Fixes distant NPCs playing Stand
  while creatureMoveCallback drives their renderer to Run.
- Switch sync loop to getLatestX/Y/Z (server-authoritative destination) for
  both the distance check and renderPos so creature positions are never stale
  from cull lag, and don't call moveInstanceTo when only entityIsMoving (no
  planarDist): the renderer's spline-driven move from creatureMoveCallback is
  already correct and shouldn't be cancelled by the per-frame sync.
- game_screen.cpp: replace scratch-built ring-burst level-up overlay with a
  simple "You have reached level X!" centered text (WoW style). The actual 3D
  visual is already handled by Renderer::triggerLevelUpEffect (LevelUp.m2).
2026-03-10 16:08:24 -07:00
Kelsi
bee4dde9b7 ui: add side action bars, fix resize positioning, and fix player nameplates
Action bars:
- Expand from 2 bars (24 slots) to 4 bars (48 slots)
- Bar 2: right-edge vertical bar (slots 24-35), off by default
- Bar 3: left-edge vertical bar (slots 36-47), off by default
- New "Interface" settings tab with toggles and offset sliders for all bars
- XP bar Y position now tracks bar 2 visibility and vertical offset

HUD resize fix:
- All HUD elements (action bars, bag bar, XP bar, cast bar, mirror timers)
  now use ImGui::GetIO().DisplaySize instead of window->getWidth/Height()
- DisplaySize is always in sync with the current frame — eliminates the
  one-frame lag that caused bars to misalign after window resize

Player nameplates:
- Show player name only on nameplate (no level number clutter)
- Fall back to "Player (level)" while name query is pending
- NPC nameplates unchanged (still show "level Name")
2026-03-10 15:56:41 -07:00
Kelsi
ec5e7c66c3 fix: derive rest state from PLAYER_BYTES_2 and add action bar 2 settings
XP bar rest state:
- isResting_ now set from PLAYER_BYTES_2 byte 3 bit 0 (rest state flag)
  on both CREATE and VALUES update object handlers
- playerRestedXp_ was missing from VALUES handler — now tracked there too
- Eliminates dependency on SMSG_SET_REST_START (wrong in WotLK opcodes.json)

Interface settings:
- New "Interface" tab in Settings window
- "Show Second Action Bar" toggle (default: on)
- Horizontal/vertical position offset sliders for bar 2
- Settings persisted to/from save file
2026-03-10 15:45:35 -07:00
Kelsi
1a370fef76 fix: chat prefix, hostile faction display, and game object looting
- Add BG_SYSTEM_NEUTRAL/ALLIANCE/HORDE chat types (0x52-0x54) and reclassify
  them as SYSTEM in the parser — prevents bogus [Say] prefix on arena/BG
  system messages
- Remove fallback [TypeName] bracket for sender-less SAY/YELL/WHISPER messages;
  only group-channel types (Party/Guild/Raid/BG) show brackets without a sender
- Remove factionTemplate != 0 guard — units with FT=0 now get setHostile() like
  any other unit (defaulting to hostile from the map default), fixing NPCs that
  appeared friendly due to unset faction template
- Enable CMSG_LOOT for WotLK type=3 (chest) game objects in addition to
  CMSG_GAMEOBJ_USE — fixes Milly's Harvest and other quest gather objects on
  AzerothCore WotLK servers
2026-03-10 15:32:04 -07:00
Kelsi
942df21c66 ui: resolve chat sender names at render time to fix [Say] prefix
When SMSG_MESSAGECHAT arrives before the entity has spawned or its
name is cached, senderName is empty and messages fell through to the
generic '[Say] message' branch. Fix:

- GameHandler::lookupName(guid): checks playerNameCache then entity
  manager (Unit subclass cast) at call time
- Chat display: resolves senderName via lookupName() at render time
  so messages show "Name says: msg" even if the name was unavailable
  when the packet was first parsed
2026-03-10 15:18:00 -07:00
Kelsi
4987388ce7 ui: show GM/AFK/DND chat tags and fix channel/bracket name display
- Display <GM>, <AFK>, <DND> prefix before sender name in all chat
  message formats based on the chatTag bitmask byte (0x04=GM, 0x01=AFK,
  0x02=DND) from SMSG_MESSAGECHAT
- Apply tagPrefix consistently across SAY/YELL/WHISPER/EMOTE/CHANNEL
  and the generic bracket-type fallback
2026-03-10 15:09:41 -07:00
Kelsi
df47d425f4 ui: fix chat type display names and outgoing whisper format
- getChatTypeName: use WoW-style mixed-case names (Party/Guild/Raid/etc.)
  instead of all-caps (PARTY/GUILD/RAID)
- WHISPER_INFORM: display "To Name: message" instead of "[To] Name: message"
  using receiverName when available, falling back to senderName
2026-03-10 15:08:21 -07:00
Kelsi
60ebb565bb rendering: fix WMO portal culling and chat message format
- wmo_renderer: pass character position (not camera position) to portal
  visibility traversal — the 3rd-person camera can orbit outside a WMO
  while the character is inside, causing interior groups to cull; render()
  now accepts optional viewerPos that defaults to camPos for compatibility
- renderer: pass &characterPosition to wmoRenderer->render() at both
  main and single-threaded call sites; reflection pass keeps camPos
- renderer: apply mount pitch/roll to rider during all flight, not just
  taxiFlight_ (fixes zero rider tilt during player-controlled flying)
- game_screen: format SAY/YELL/WHISPER/EMOTE using WoW-style "Name says:"
  instead of "[SAY] Name:" bracket prefix
2026-03-10 14:59:02 -07:00
Kelsi
920d6ac120 physics: sync camera pitch to movement packets and mount tilt during flight
- Add setMovementPitch() and isSwimming() to GameHandler
- In the per-frame sync block, derive the pitch angle from the camera's
  forward vector (asin of the Z component) and write it to movementInfo.pitch
  whenever FLYING or SWIMMING flags are set — the server includes the pitch
  field in those packets, so sending 0 made other players see the character
  flying perfectly flat even when the camera was pitched
- Also tilt the mount model (setMountPitchRoll) to match the flight direction
  during player-controlled flight, and reset to 0 when not flying
2026-03-10 14:46:17 -07:00
Kelsi
132598fc88 physics: send MSG_MOVE_START/STOP_ASCEND and START_DESCEND during flight
When flyingActive_, detect Space/X key transitions and emit proper flight
vertical movement opcodes so the server (and other players) see the
correct ascending/descending animation state:

- MSG_MOVE_START_ASCEND  (Space pressed while flying)  → sets ASCENDING flag
- MSG_MOVE_STOP_ASCEND   (Space released while flying) → clears ASCENDING flag
- MSG_MOVE_START_DESCEND (X pressed while flying)      → clears ASCENDING flag
- MSG_MOVE_STOP_ASCEND   (X released while flying)     → clears vertical state

Track wasAscending_/wasDescending_ member state to detect transitions.
Also clear lingering vertical state when leaving flight mode.
2026-03-10 14:32:30 -07:00
Kelsi
a9ddfe70c2 physics: sync server turn rate and fix SPLINE speed handlers
- Add getServerTurnRate() accessor and turnRateOverride_ field so the
  keyboard turn speed respects SMSG_FORCE_TURN_RATE_CHANGE from server
- Convert rad/s → deg/s before applying to camera yaw logic
- Fix SMSG_SPLINE_SET_RUN_BACK/SWIM/FLIGHT/FLIGHT_BACK/SWIM_BACK/WALK/
  TURN_RATE handlers: all previously discarded the value; now update the
  corresponding serverXxxSpeed_ / serverTurnRate_ field when GUID matches
  playerGuid (camera controller syncs these every frame)
2026-03-10 14:18:25 -07:00
Kelsi
e2f65dfc59 physics: add server flight-back speed override to CameraController
SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE was already ACK'd and stored in
serverFlightBackSpeed_, but the value was never accessible or synced
to the CameraController. Backward flight movement always used forward
flight speed (flightSpeedOverride_), making it faster than the server
intended.

- Add getServerFlightBackSpeed() accessor in GameHandler
- Add flightBackSpeedOverride_ field and setter in CameraController
- Apply it in the fly movement block: backward-only flight uses the
  back speed; forward or strafing uses the forward speed as WoW does
- Fallback: 50% of forward flight speed when override is unset
- Sync per-frame in application.cpp alongside the other speed overrides
2026-03-10 14:05:50 -07:00
Kelsi
a33f635490 physics: add server swim-back speed override to CameraController
Backward swimming was using 50% of forward swim speed as a hardcoded
fallback. Wire up the server-authoritative swim back speed so Warlock
Dark Pact, buffs, and server-forced speed changes all apply correctly
when swimming backward.

- game_handler.hpp: add getServerSwimBackSpeed() accessor
- camera_controller.hpp: add swimBackSpeedOverride_ field + setter
- camera_controller.cpp: apply swimBackSpeedOverride_ when player
  swims backward without forward input; fall back to 50% of swim speed
- application.cpp: sync swim back speed each frame
2026-03-10 13:51:47 -07:00
Kelsi
23293d6453 physics: implement HOVER movement flag physics in CameraController
When the server sets MovementFlags::HOVER (SMSG_MOVE_SET_HOVER), the
player now floats 4 yards above the nearest ground surface instead of
standing on it. Uses the existing floor-snap path with a HOVER_HEIGHT
offset applied to the snap target.

- game_handler.hpp: add isHovering() accessor (reads HOVER flag from
  movementInfo.flags, which is already set by handleForceMoveFlagChange)
- camera_controller.hpp: add hoverActive_ field and setHoverActive()
- camera_controller.cpp: apply HOVER_HEIGHT = 4.0f offset at floor snap
- application.cpp: sync hover state each frame alongside other movement
  states (gravity, feather fall, water walk, flying)
2026-03-10 13:39:23 -07:00
Kelsi
56ec49f837 physics: sync all server movement speeds to CameraController
Previously only run speed was synced. Now all server-driven movement
speeds are forwarded to the camera controller each frame:
- runSpeedOverride_: server run speed (existing)
- walkSpeedOverride_: server walk speed (Ctrl key movement)
- swimSpeedOverride_: swim speed (Swim Form, Engineering fins)
- flightSpeedOverride_: flight speed (epic vs normal flying mounts)
- runBackSpeedOverride_: back-pedal speed

Each uses the server value when non-zero/sane, falling back to the
hardcoded WoW default constant otherwise.
2026-03-10 13:28:53 -07:00
Kelsi
a1ee9827d8 physics: apply server flight speed to flying mount movement
serverFlightSpeed_ (from SMSG_FORCE_FLIGHT_SPEED_CHANGE) was stored but
never synced to CameraController. Add getServerFlightSpeed() accessor,
flightSpeedOverride_ field, and use it in the flying physics path so
normal vs epic flying mounts actually move at their correct speeds.
2026-03-10 13:25:10 -07:00
Kelsi
27d18b2189 physics: implement player-controlled flying mount physics
When CAN_FLY + FLYING movement flags are both set (flying mounts, Druid
Flight Form), the CameraController now uses 3D pitch-following movement
instead of ground physics:
- Forward/back follows the camera's 3D look direction (ascend when
  looking up, descend when looking down)
- Space = ascend vertically, X (while mounted) = descend
- No gravity, no grounding, no jump coyote time
- Fall-damage checks suppressed (grounded=true)

Also wire up all remaining server movement state flags to CameraController:
- Feather Fall: cap terminal velocity at -2 m/s
- Water Walk: clamp to water surface, skip swim entry
- Flying: 3D movement with no gravity

All states synced each frame from GameHandler via isPlayerFlying(),
isFeatherFalling(), isWaterWalking(), isGravityDisabled().
2026-03-10 13:23:38 -07:00
Kelsi
1853e8aa56 physics: implement Water Walk movement state tracking and surface clamping
SMSG_MOVE_WATER_WALK / SMSG_MOVE_LAND_WALK now correctly set/clear
WATER_WALK (0x00008000) in movementInfo.flags, ensuring the flag is
included in movement ACKs sent to the server.

In CameraController, when waterWalkActive_ is set and the player is
at or above the water surface (within 0.5 units), clamp them to the
water surface and mark as grounded — preventing water entry and allowing
them to walk across the water surface as the spell intends.
2026-03-10 13:18:04 -07:00
Kelsi
0b99cbafb2 physics: implement feather fall and water walk movement flag tracking
Feather Fall (SMSG_MOVE_FEATHER_FALL / SMSG_MOVE_NORMAL_FALL):
- Add FEATHER_FALL = 0x00004000 to MovementFlags enum
- Fix handlers to set/clear the flag instead of passing flag=0
- Cap downward terminal velocity at -2.0 m/s in CameraController when
  feather fall is active (Slow Fall, Parachute, etc.)

All three handlers now correctly propagate server movement state flags
that were previously acknowledged without updating any local state.
2026-03-10 13:14:52 -07:00