115 Commits

Author SHA1 Message Date
Kelsi
a8d7582f8a feat: gate and explain target-aura-state abilities (Execute et al.)
Load Spell.dbc TargetAuraState (added to every expansion layout at its
verified column) into the spell cache with getSpellTargetAuraState. The
action bar now dims abilities whose target isn't in the required aura state
(e.g. Execute below 20% health), with a tooltip naming the requirement, and
SMSG_CAST_FAILED result 111 (Target aurastate) now yields 'Target must be
below 20% health.' instead of the opaque protocol label.
2026-07-23 20:03:06 -07:00
Kelsi
9b2931ea87 feat: show real achievement icons in the achievements window
Load Achievement.dbc's IconID (added to the WotLK DBC layout, field 42) into
the achievement cache and resolve it through SpellIcon.dbc to the artwork.
The earned list now renders a bordered 32px icon with the name and points
beside it, replacing the gold-star glyph; a star placeholder fills the slot
while an icon streams in or when one is absent. Icon textures are lazily
BLP-loaded and cached in WindowManager with a per-frame upload cap.
2026-07-23 19:26:28 -07:00
Kelsi
56c309e306 fix: correct Spell Tooltip/Rank columns and resolve talent description tokens
WotLK Tooltip (139->187) and TBC Rank (136->144) / Tooltip (154->178)
layout indices assumed the wrong locale-block stride and landed on empty
columns; point them at the verified enUS strings.

Rework the talent description formatter into a real $-token resolver: it
handles $s/$o/$m/$M base points and $d durations with cross-spell
$<spellId> references (e.g. $14201d), plus $l/$g plural/gender forms,
using live spell data via GameHandler. Unresolvable tokens ($h proc chance,
$t period) are stripped cleanly instead of rendering raw, so Enrage now
reads 'a 4% damage bonus for 12 sec' rather than '$14201s1% ... $14201d'.
2026-07-23 18:36:38 -07:00
Kelsi
b69a679fdc feat: talent tooltips show substituted effect descriptions
The talent panel read Spell.dbc's Tooltip column, which the layouts pointed
at an empty locale field, so hovering a talent showed only name and rank. Add
the correct Description column to every expansion's DBC layout (verified
against each Spell.dbc's string blocks) and read it in the talent screen,
substituting $s/$o magnitude tokens with the rank's EffectBasePoints so
descriptions read with concrete numbers. Unlearned talents show their rank-1
effect under 'Effect:'.
2026-07-23 18:26:21 -07:00
Kelsi
d00f20b3e1 fix(gameplay): polish worker and consumable presentation
Parse per-animation M2 color alpha tracks across vanilla and WotLK layouts so hidden lumberjack bundle meshes remain culled until their authored death sequence.

Keep scripted worker emotes and display swaps coherent across movement and async model loads. Rework food and drink presentation around the seated aura lifecycle, repeat consumption audio, preserve real cast animations on auto-stand, and retain potion swigs.

Add server-driven intoxication movement, camera, and post-process effects plus focused regression coverage for M2 alpha tracks and consumable classification.
2026-07-17 00:48:21 -07:00
Kelsi
aaebd4fa50 fix(auth): restore legacy proof security-flags byte, default Turtle to protocol 8
PR #101 dropped the trailing security-flags byte from the legacy LOGON_PROOF,
but mangos-family realmd reads the 1.11+ proof as a fixed-size struct that
includes it, so protocol-3 logins against stock vanilla realms would stall.
Also flip the Turtle profile to auth protocol 8 (vmangos-derived servers
require it) so the first login attempt no longer fails and retries; the
vanilla-family fallback chain still tries protocol 3 second.
2026-07-16 00:10:56 -07:00
Josh Anderson
0857b6d6ed merge: sync upstream animation and inventory fixes 2026-07-13 22:00:33 -05:00
Kelsi
4fa0dd250a Add isolated asset profiles and Turtle 1.18.1 support 2026-07-13 15:47:34 -07:00
Josh Anderson
c558668893 fix(auth): use SRP protocol v8 for classic expansion profile
protocolVersion 3 was rejected during auth against a VMangos classic
server - the working live-tested value is 8, matching tbc/turtle's
existing protocol selection.
2026-07-13 16:31:19 -05:00
Kelsi
bcd032fde7 fix(dbc): read enchant names from the right SpellItemEnchantment column
The name column moved across expansions (Vanilla 10, TBC 13, WotLK 14), but the
layouts and every caller used field 8. That column holds an integer, which
getString() then treats as a string-block offset, so enchant names came back
garbled mid-string ("Sharpened (+2 Damage)" surfaced as "ockbiter 3"). Resolve
the column from the record width via detectEnchantmentNameField() and use it at
all four call sites.
2026-07-12 09:10:02 -07:00
Kelsi
e16b165512 fix(items): send TARGET_FLAG_ITEM so sharpening stones and oils apply
Item-enhancement consumables cast a spell onto another item, but CMSG_USE_ITEM
only ever wrote a unit or self target, so the server dropped the cast. Using one
now reads the on-use spell's Spell.dbc Targets mask and, when TARGET_FLAG_ITEM is
set, arms a targeting cursor whose next item click sends the use against that
item's GUID.
2026-07-12 09:09:49 -07:00
Kelsi
e9b6973468 fix(gameplay): correct ability state, item casts, and quest tracking
Fix several gameplay paths where client-side state diverged from server
semantics.

Ability availability now checks the resource pool declared by Spell.dbc
instead of comparing only against the player's currently displayed power
type. This makes action-bar greying work for mana, rage, focus, energy,
runes, and runic power without class-name assumptions, and supports reactive
aura-state abilities such as Overpower.

Improve bandage handling:
  - translate the hidden Recently Bandaged aura failure into a clear message
  - report bandage interruptions with actionable text
  - apply spell-aware failure text across all cast-failure packet paths
  - synchronize a stationary movement state before CMSG_USE_ITEM for bandages
    so stale server-side movement does not falsely interrupt the channel

Fix quest objective tracking by decoding quest-query objectives with the
correct expansion-specific packet layouts. Classic and Turtle, TBC, and
Wrath now use the proper objective offsets and structures, game-object IDs
are decoded correctly, implausible objective data is rejected, and stale
corrupted tracker entries are removed when valid quest data arrives.

Fix thrown-weapon use:
  - keep Throw out of the eight-yard melee range gate
  - route Throw through the ranged-weapon animation path
  - avoid duplicate melee/special attack animations
  - correct thrown and ranged-right inventory type constants used by
    action-bar range checks

Build passes. Packet, entity, animation capability, and combat FSM tests
pass.
2026-07-11 19:03:02 -07:00
Josh Anderson
ee410175e4 fix(tbc): improve playtest gameplay handling 2026-07-05 19:16:14 -05:00
Josh Anderson
9a81899769 fix(tbc): parse update objects and container fields 2026-07-05 12:36:02 -05:00
Kelsi
293f42d2b2 fix(dbc): correct TBC Spell.dbc DurationIndex (was aliased with RangeIndex at 40)
Data/expansions/tbc/dbc_layouts.json had DurationIndex=40 — but that
column is RangeIndex in TBC 2.4.3. DurationIndex is at column 34.

Per cmangos mangos-tbc src/game/Server/DBCStructure.h:
    uint32  CastingTimeIndex;   // 22
    uint32  DurationIndex;      // 34
    uint32  rangeIndex;         // 40

Visible impact prior to this fix on TBC servers: every spell-duration
lookup read the spell's RangeIndex value (an index into SpellRange.dbc)
instead of its actual DurationIndex (an index into SpellDuration.dbc).
Cast durations and aura durations would have come back as nonsense
values — most likely zero or wildly incorrect for spells whose range
index happens to be a valid duration row.

Found by scanning all dbc_layouts.json files for duplicate field
indices within a single DBC entry — the (DurationIndex, RangeIndex,
40) collision was the only one across all four expansions.

Classic (Duration=40 Range=33) and WotLK (Duration=40 Range=49)
remained intact; only TBC had the alias.
2026-05-15 04:04:01 -07:00
Kelsi
c55a703835 fix(dbc): add Faction.Name field to all expansion layouts
item_tooltip_renderer.cpp:378 queries Faction.dbc Name field to
resolve faction IDs to localized names for the 'Requires X Reputation'
line in item tooltips. None of the four dbc_layouts.json defined a
Name entry under Faction; the code's ternary fallback (20u) only
fired when the entire Faction layout was null. With the layout
present, (*layout)['Name'] returned DBC_FIELD_INVALID (0xFFFFFFFF)
and dbc->getString(r, 0xFFFFFFFF) read out of bounds.

Result: s_factionNames never populated → every item with a reputation
requirement displayed 'Unknown Faction'.

Canonical Name column index per Faction.dbc DBC format strings
(string offset to the English-locale entry):

  Vanilla 1.12:  19   (cmangos-classic DBCfmt.h FactionEntryfmt:
                       "niiiiiiiiiiiiiiiiiissssssssxxxxxxxxxx")
  TBC 2.4.3:     23   (cmangos-tbc SQLStorages.cpp FactionEntryfmt:
                       "iiiiiiiiiiiiiiiiiiiffiissssssssssssssss")
  WotLK 3.3.5a:  23   (TrinityCore 3.3.5 DBCfmt.h FactionEntryfmt:
                       "niiiiiiiiiiiiiiiiiiffiissssssssssssssssxxxxxxxxxxxxxxxxxx")

Note: in 1.12 the row layout omits ReputationFlags[4] that TBC/WotLK
added, so the Name string offset moves from 19 → 23 across the
TBC bump.

Same root-cause pattern as bd5e9aa2 and 50e31ab0 (DBC field referenced
by code but missing from layout JSON → operator[] sentinel beats
the ternary fallback).
2026-05-15 02:25:41 -07:00
Kelsi
50e31ab0eb fix(dbc): add ItemDisplayInfo.RightModel and RightModelTexture to all expansions
entity_spawner.cpp:1832-1835 queries these for shoulder-piece M2
attachment (shoulders have two models — left at attachment point 5
and right at attachment point 6):

    const uint32_t leftModelField  = idiL ? (*idiL)["LeftModel"]        : 1u;
    const uint32_t rightModelField = idiL ? (*idiL)["RightModel"]       : 2u;
    const uint32_t leftTexFieldS   = idiL ? (*idiL)["LeftModelTexture"] : 3u;
    const uint32_t rightTexFieldS  = idiL ? (*idiL)["RightModelTexture"]: 4u;

None of the four dbc_layouts.json files defined RightModel or
RightModelTexture. The ternary returns the layout lookup result when
idiL is non-null — and DBCFieldMap::operator[] on a missing field
returns DBC_FIELD_INVALID (0xFFFFFFFF), NOT the intended fallback.
So shoulder right-side M2 model and texture loaded from field
0xFFFFFFFF (out of bounds, getUInt32 returns 0) and the right
shoulder pad never rendered on any character.

Added the standard pairing values (RightModel=2, RightModelTexture=4)
to all four expansion layouts — these match the canonical
ItemDisplayInfo.dbc field order and the code's intended fallbacks.

Same root cause as bd5e9aa2 (SpellRange.MinRange) — the
'tnL ? (*tnL)["Field"] : N' pattern silently breaks when the field
is missing from the JSON but the DBC is present, because the
sentinel from operator[] beats the fallback N.
2026-05-15 02:04:38 -07:00
Kelsi
bd5e9aa2c2 fix(dbc): add SpellRange.MinRange to all expansion layouts
application.cpp:438 queries SpellRange.MinRange to populate the
spell-range map used by minimum-distance casting checks. None of the
four dbc_layouts.json files defined MinRange, so the lookup returned
the DBC_FIELD_INVALID sentinel (0xFFFFFFFF); rDbc->getFloat(i, 0xFFFFFFFF)
then read out of bounds and returned 0. Minimum-range checks were
silently dead on every expansion.

Canonical SpellRange.dbc layouts (per spellbook_screen.cpp:71-74):

  Classic 1.12:  0=ID, 1=MinRange,       2=MaxRange
  TBC / WotLK:   0=ID, 1=MinRangeFriendly, 2=MinRangeHostile,
                       3=MaxRangeFriendly, 4=MaxRangeHostile

The existing MaxRange values (classic 2, TBC/WotLK 4) follow the
'Hostile' convention; MinRange is wired the same way (1 for Vanilla,
2 for TBC/WotLK).

Visible effect: spells with a non-zero minimum range (hunter ranged
abilities, Pyroblast, etc.) will now correctly trigger 'You are too
close' errors and target-validity checks at the client level.
2026-05-15 01:54:16 -07:00
Kelsi
8b4c67bab6 fix(opcodes): map 0x115 to SMSG_INSPECT on Classic (was mislabeled SMSG_INSPECT_RESULTS_UPDATE)
Per cmangos / vmangos Vanilla 1.12 opcode tables:
  SMSG_INSPECT = 0x115 (cmangos Opcodes.h:315, vmangos Opcodes_1_12_1.h:280)

That wire slot was REPURPOSED in TBC to SMSG_INSPECT_RESULTS_UPDATE
(with a different packet body). The Classic opcode table inherited the
post-TBC name, which meant:

- Server sends 0x115 with the Vanilla SMSG_INSPECT body
  (PackedGUID + 19 uint32 itemEntries — EQUIPMENT_SLOT_END=19).
- fromWire(0x115) returned LogicalOpcode::SMSG_INSPECT_RESULTS_UPDATE
  on Classic.
- social_handler.cpp:128 routed the bytes to handleInspectResults(),
  which parses the WotLK-style format and misread the packet.
- The handler at game_handler_packets.cpp:1902 — which has the
  correct Classic 1.12 parser and an explicit comment 'only reachable
  on Classic servers' — was dead code on Classic.

Fixing the JSON name routes wire 0x115 to the Classic-aware handler.
TBC and WotLK JSONs are untouched (SMSG_INSPECT_RESULTS_UPDATE = 0x115
is correct for those expansions).

Turtle inherits classic via _extends, so it gets the fix automatically.

Not patched in this commit:
- SMSG_SET_REST_START / SMSG_QUEST_FORCE_REMOVE both legitimately
  exist at 0x21E across forks (vmangos uses one name, AzerothCore/TC
  uses the other). quest_handler.cpp:685-700 already disambiguates
  by expansion at runtime, so the existing mapping is defensible.
- SMSG_LEVELUP_INFO_ALT — appears to be a project-invented name with
  no upstream wire value. Left alone.
2026-05-15 01:36:57 -07:00
Kelsi
99f73b6ffa fix(update-fields): correct UNIT_NPC_EMOTESTATE across all four expansions
UNIT_NPC_EMOTESTATE is read in entity_controller.cpp:551 for stationary
NPC looping animations (sleeping, talking, working, etc.) and in
animation_callback_handler.cpp:323.

- classic, turtle, tbc: field was missing entirely — fieldIndex() was
  returning the 0xFFFF sentinel, so NPC idle emote animations never
  played on those expansions.
- wotlk: field was set to 164, which is past UNIT_END (148) — that
  index falls inside the PLAYER_ range, so reading it would have
  pulled an unrelated player-fields value rather than the unit emote
  state.

Authoritative sources:
  Vanilla 1.12:  148   (vmangos UpdateFields_1_12_1.h:93,
                       OBJECT_END + 0x8E)
  TBC 2.4.3:     169   (cmangos mangos-tbc UpdateFields.h:118,
                       OBJECT_END + 0xA3)
  WotLK 3.3.5a:  83    (TrinityCore 3.3.5 UpdateFields.h:140,
                       OBJECT_END + 0x4D — adjacent to UNIT_NPC_FLAGS=82)

Python collision check after patch: no UNIT_* aliasing in any file.
2026-05-15 01:15:03 -07:00
Kelsi
644cfcc8c1 fix(update-fields): correct multiple wrong vanilla 1.12 indices on classic/turtle
The classic and turtle update_fields.json files had several wrong
indices that aliased fields together and misread the unit record.
Cross-referenced vmangos UpdateFields_1_12_1.h
(github.com/vmangos/core, OBJECT_END=6) and corrected:

- UNIT_FIELD_BYTES_1:        133 -> 138 (was colliding with MOUNTDISPLAYID)
- UNIT_FIELD_STAT0..STAT4:   138..142 -> 150..154 (the old slot held
                              pet fields PETNUMBER/PET_NAME_TIMESTAMP/
                              PETEXPERIENCE/PETNEXTLEVELEXP, not stats)
- UNIT_FIELD_RESISTANCES:    154 -> 155 (off by one — STAT4 lives at 154)
- UNIT_FIELD_NATIVEDISPLAYID: added (was missing) -> 132

The verified other indices (DISPLAYID=131, MOUNTDISPLAYID=133,
DYNAMIC_FLAGS=143, NPC_FLAGS=147, BYTES_0=36) all matched vmangos
and were left alone.

Practical impact on Classic/Turtle prior to this fix:
- entity_controller.cpp:548-553 read MOUNTDISPLAYID and BYTES_1 from
  the same slot — mount/dismount detection and shapeshift form
  detection would race and clobber each other.
- All STAT* readers were reading pet-related fields instead of stats,
  so character-sheet stats and stat-derived UI on Classic/Turtle
  would be wrong/zero.
- UNIT_FIELD_RESISTANCES readers got STAT4 instead of armor resist.

Python collision check after patch: no UNIT_* collisions in either
file.
2026-05-15 00:55:07 -07:00
Pavel Okhlopkov
b79d9b8fea feat(rendering): implement spell visual effects with bone-tracked ribbons and particles
Add complete spell visual pipeline resolving the DBC chain
(Spell → SpellVisual → SpellVisualKit → SpellVisualEffectName → M2)
with precast/cast/impact phases, bone-attached positioning, and
automatic dual-hand mirroring.

Ribbon rendering fixes:
- Parse visibility track as uint8 (was read as float, suppressing
  all ribbon edges due to ~1.4e-45 failing the >0.5 check)
- Filter garbage emitters with bone=UINT_MAX unconditionally
- Guard against NaN spine positions from corrupt bone data
- Resolve ribbon textures via direct index, not textureLookup table
- Fall back to bone 0 when ribbon bone index is out of range

Particle rendering fixes:
- Reduce spell particle scale from 5x to 1.5x (was oversized)
- Exempt spell effect instances from position-based deduplication

Spell handler integration:
- Trigger precast visuals on SMSG_SPELL_START with server castTimeMs
- Trigger cast/impact visuals on SMSG_SPELL_GO
- Cancel precast visuals on cast interrupt/failure/movement

M2 classifier expansion:
- Add AmbientEmitterType enum for sound system integration
- Add 20+ foliage tokens, 4 spell effect tokens, isSmallFoliage flag
- Add markModelAsSpellEffect() to override disableAnimation

DBC layouts:
- Add SpellVisualID field to Spell.dbc for all expansion configs

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
2026-04-07 11:27:59 +03:00
Kelsi
0e308cf5a1 chore: remove HelloWorld test addon 2026-04-05 02:39:10 -07:00
Paul
e58f9b4b40 feat(animation): 452 named constants, 30-phase character animation state machine
Add animation_ids.hpp/cpp with all 452 WoW animation ID constants (anim::STAND,
anim::RUN, anim::FIRE_BOW, ... anim::FLY_BACKWARDS, etc.), nameFromId() O(1)
lookup, and flyVariant() compact 218-element ground→FLY_* resolver.

Expand AnimationController into a full state machine with 20+ named states:
spell cast (directed→omni→cast fallback chain, instant one-shot release),
hit reactions (WOUND/CRIT/DODGE/BLOCK/SHIELD_BLOCK), stun, wounded idle,
stealth animation substitution, loot, fishing channel, sit/sleep/kneel
down→loop→up transitions, sheathe/unsheathe combat enter/exit, ranged weapons
(BOW/GUN/CROSSBOW/THROWN with reload states), game object OPEN/CLOSE/DESTROY,
vehicle enter/exit, mount flight directionals (FLY_LEFT/RIGHT/UP/DOWN/BACKWARDS),
emote state variants, off-hand/pierce/dual-wield alternation, NPC
birth/spawn/drown/rise, sprint aura override, totem idle, NPC greeting/farewell.

Add spell_defines.hpp with SpellEffect (~45 constants) and SpellMissInfo
(12 constants) namespaces; replace all magic numbers in spell_handler.cpp.

Add GAMEOBJECT_BYTES_1 to update field table (all 4 expansion JSONs) and wire
GameObjectStateCallback. Add DBC cross-validation on world entry.

Expand tools/_ANIM_NAMES from ~35 to 452 entries in m2_viewer.py and
asset_pipeline_gui.py. Add tests/test_animation_ids.cpp.

Bug fixes included:
- Stand state 1 was animating READY_2H(27) — fixed to SITTING(97)
- Spell casts ended freeze-frame — add one-shot release animation
- NPC 2H swing probe chain missing ATTACK_2H_LOOSE (polearm/staff)
- Chair sits (states 2/4/5/6) incorrectly played floor-sit transition
- STOP(3) used for all spell casts — replaced with model-aware chain
2026-04-04 23:02:53 +03:00
Kelsi
1379e74c40 fix(dbc): runtime detection for ItemDisplayInfo texture field indices
Revert static JSON layout changes (15-22 back to 14-21) since WotLK
loads the Classic 23-field DBC. Add getItemDisplayInfoTextureFields()
helper that detects field count at runtime and adjusts the texture
base index accordingly (14 for 23-field, 15 for 25-field).
2026-04-03 22:05:38 -07:00
Kelsi
3111fa50e8 fix(dbc): correct ItemDisplayInfo texture field indices for TBC/WotLK
TBC and WotLK have 25-field ItemDisplayInfo.dbc (vs 23 in Classic/Turtle)
with 2 extra fields before the texture region block. The texture fields
start at index 15 (not 14), shifting all 8 regions by +1.

This caused every equipment texture to be composited into the wrong body
region — e.g. LegLower textures landing in FootTexture, belt textures in
LegLowerTexture instead of LegUpperTexture ("everything shifted by 1").

Verified against raw DBC binary data: Classic field[14]=ArmUpper,
TBC/WotLK field[15]=ArmUpper.
2026-04-03 21:52:20 -07:00
Kelsi
11ecc475c8 feat: resolve spell \$d duration to real seconds from SpellDuration.dbc
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.
2026-03-23 01:00:18 -07:00
Kelsi
a5aa1faf7a feat: resolve spell \$s1/\$s2/\$s3 to real DBC damage/heal 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.).
2026-03-23 00:51:19 -07:00
Kelsi
587c0ef60d feat: track shapeshift form and fire UPDATE_SHAPESHIFT_FORM events
Add UNIT_FIELD_BYTES_1 to all expansion update field tables (Classic=133,
TBC/WotLK=137). Byte 3 of this field contains the shapeshift form ID
(Bear=1, Cat=3, Travel=4, Moonkin=31, Tree=36, Battle Stance=17, etc.).

Track form changes in the VALUES update handler and fire
UPDATE_SHAPESHIFT_FORM + UPDATE_SHAPESHIFT_FORMS events when the
form changes. This enables stance bar addons and druid form tracking.

New Lua functions:
- GetShapeshiftForm() — returns current form ID (0 = no form)
- GetNumShapeshiftForms() — returns form count by class (Warrior=3,
  Druid=6, DK=3, Rogue=1, Priest=1, Paladin=3)
2026-03-22 22:12:17 -07:00
Kelsi
e9ce062112 fix: restore correct CharSections.dbc field indices for character textures
PR #19 (572bb4ef) swapped CharSections.dbc field indices, placing
Texture1-3 at fields 4-6 and VariationIndex/ColorIndex at 8-9. Binary
analysis of the actual DBC files (Classic, TBC, Turtle — all identical
layout, no WotLK-specific override) confirms the correct order is:

  Field 4 = VariationIndex
  Field 5 = ColorIndex
  Field 6 = Texture1 (string)
  Field 7 = Texture2 (string)
  Field 8 = Texture3 (string)
  Field 9 = Flags

With the wrong indices, VariationIndex/ColorIndex reads returned string
offsets (garbage values that never matched), so all CharSections lookups
failed silently — producing white untextured character models at the
login screen and in-world.

Fixes all 4 expansion JSON layouts, hardcoded fallbacks in
character_preview.cpp, application.cpp, and character_create_screen.cpp.
Also handles the single-layer edge case (body skin only, no face/underwear)
by loading the texture directly instead of skipping compositing.
2026-03-22 15:22:25 -07:00
Paul
572bb4ef36 fix preview white textutes 2026-03-22 21:38:56 +03:00
Kelsi
062cfd1e4a feat: add SavedVariables persistence for Lua addons
Addons can now persist data across sessions using the standard WoW
SavedVariables pattern:

1. Declare in .toc: ## SavedVariables: MyAddonDB
2. Use the global in Lua: MyAddonDB = MyAddonDB or {default = true}
3. Data is automatically saved on logout and restored on next login

Implementation:
- TocFile::getSavedVariables() parses comma-separated variable names
- LuaEngine::loadSavedVariables() executes saved .lua file to restore globals
- LuaEngine::saveSavedVariables() serializes Lua tables/values to valid Lua
- Serializer handles tables (nested), strings, numbers, booleans, nil
- Save triggered on PLAYER_LEAVING_WORLD and AddonManager::shutdown()
- Files stored as <AddonDir>/<AddonName>.lua.saved

Updated HelloWorld addon to track login count across sessions.
2026-03-20 12:22:50 -07:00
Kelsi
c284a971c2 feat: add CreateFrame with RegisterEvent/SetScript for WoW addon pattern
Implement the core WoW frame system that nearly all addons use:

- CreateFrame(type, name, parent, template) — creates a frame table
  with metatable methods, optionally registered as a global by name
- frame:RegisterEvent(event) — register frame for event dispatch
- frame:UnregisterEvent(event) — unregister
- frame:SetScript(type, handler) — set OnEvent/OnUpdate/etc handlers
- frame:GetScript(type) — retrieve handlers
- frame:Show()/Hide()/IsShown()/IsVisible() — visibility state
- frame:GetName() — return frame name

Event dispatch now fires both global RegisterEvent handlers AND
frame OnEvent scripts, matching WoW's dual dispatch model.

Updated HelloWorld to use standard WoW addon pattern:
  local f = CreateFrame("Frame", "MyFrame")
  f:RegisterEvent("PLAYER_ENTERING_WORLD")
  f:SetScript("OnEvent", function(self, event, ...) end)
2026-03-20 11:46:04 -07:00
Kelsi
510f03fa32 feat: add WoW event system for Lua addons
Implement the WoW-compatible event system that lets addons react to
gameplay events in real-time:

- RegisterEvent(eventName, handler) — register a Lua function for an event
- UnregisterEvent(eventName, handler) — remove a handler
- fireEvent() dispatches events to all registered handlers with args

Currently fired events:
- PLAYER_ENTERING_WORLD — after addons load and world entry completes
- PLAYER_LEAVING_WORLD — before logout/disconnect

Events are stored in a __WoweeEvents Lua table, dispatched via
LuaEngine::fireEvent() which is called from AddonManager::fireEvent().
Error handling logs Lua errors without crashing.

Updated HelloWorld addon to use RegisterEvent for world entry/exit.
2026-03-20 11:23:38 -07:00
Kelsi
7da1f6f5ca feat: add core WoW Lua API functions for addon development
Add 13 WoW-compatible Lua API functions that addons can call:

Unit API: UnitName, UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax,
UnitLevel, UnitExists, UnitIsDead, UnitClass (supports "player",
"target", "focus", "pet" unit IDs)

Game API: GetMoney, IsInGroup, IsInRaid, GetPlayerMapPosition

Updated HelloWorld addon to demonstrate querying player state.
2026-03-20 11:17:15 -07:00
Kelsi
290e9bfbd8 feat: add Lua 5.1 addon system with .toc loader and /run command
Foundation for WoW-compatible addon support:

- Vendor Lua 5.1.5 source as a static library (extern/lua-5.1.5)
- TocParser: parses .toc files (## directives + file lists)
- LuaEngine: Lua 5.1 VM with sandboxed stdlib (no io/os/debug),
  WoW-compatible print() that outputs to chat, GetTime() stub
- AddonManager: scans Data/interface/AddOns/ for .toc files,
  loads .lua files on world entry, skips LoadOnDemand addons
- /run <code> slash command for inline Lua execution
- HelloWorld test addon that prints to chat on load

Integration: AddonManager initialized after asset manager, addons
loaded once on first world entry, reset on logout. XML frame
parsing is deferred to a future step.
2026-03-20 11:12:07 -07:00
Kelsi
f88d90ee88 feat: track and display honor/arena points from update fields
Add PLAYER_FIELD_HONOR_CURRENCY and PLAYER_FIELD_ARENA_CURRENCY to the
update field system for WotLK (indices 1422/1423) and TBC (1505/1506).
Parse values from both CREATE_OBJECT and VALUES update paths, and show
them in the character Stats tab under a PvP Currency section.
2026-03-20 04:36:30 -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
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
Kelsi
6ede9a2968 refactor: derive turtle opcodes from classic 2026-03-15 02:55:05 -07:00
Kelsi
0b6265bc55 fix: align turtle protocol compatibility 2026-03-15 01:47:36 -07:00
Kelsi
b0fafe5efa fix: stabilize turtle world entry session handling 2026-03-15 01:21:23 -07:00
Kelsi
2b79f9d121 feat: add spell power and healing bonus to WotLK character stats
Tracks PLAYER_FIELD_MOD_DAMAGE_DONE_POS (7 schools at field 1171) and
PLAYER_FIELD_MOD_HEALING_DONE_POS (field 1192) from server update fields.

getSpellPower() returns the max damage bonus across magic schools 1-6.
getHealingPower() returns the raw healing bonus.

Both values displayed in the character screen Combat section alongside
the previously added attack power, dodge, parry, crit, and rating fields.
2026-03-13 08:37:55 -07:00
Kelsi
c0ffca68f2 feat: track and display WotLK server-authoritative combat stats
Adds update field tracking for WotLK secondary combat statistics:
- UNIT_FIELD_ATTACK_POWER / RANGED_ATTACK_POWER (fields 123, 126)
- PLAYER_DODGE/PARRY/BLOCK/CRIT_PERCENTAGE (fields 1025-1029)
- PLAYER_RANGED_CRIT_PERCENTAGE, PLAYER_SPELL_CRIT_PERCENTAGE1 (1030, 1032)
- PLAYER_FIELD_COMBAT_RATING_1 (25 slots at 1231, hit/expertise/haste/etc.)

Both CREATE_OBJECT and VALUES update paths now populate these fields.
The Character screen Stats tab shows them when received from the server,
with graceful fallback when not available (Classic/TBC expansions).

Field indices verified against AzerothCore 3.3.5a UpdateFields.h.
2026-03-13 08:35:18 -07:00
Kelsi
ed02f5872a feat: show LFG dungeon name in Dungeon Finder queue messages
Add LFGDungeons.dbc cache (loadLfgDungeonDbc / getLfgDungeonName) and
use it to enrich three LFG chat messages in WotLK:
- handleLfgJoinResult: "Joined the queue for Culling of Stratholme."
- handleLfgProposalUpdate case 1: "Group found for Halls of Lightning!"
- handleLfgProposalUpdate case 2: "A group has been found for ... Accept or decline."
Falls back to generic text when DBC is unavailable or dungeon ID unknown.
2026-03-13 08:14:47 -07:00
Kelsi
18d0e6a252 feat: use UNIT_FIELD_AURAFLAGS to correctly classify Classic buffs vs debuffs
Classic WoW stores aura flags in UNIT_FIELD_AURAFLAGS (12 uint32 fields
packed 4 bytes per uint32, one byte per aura slot). Flag bit 0x02 = harmful
(debuff), 0x04 = helpful (buff).

- Add UNIT_FIELD_AURAFLAGS to update_field_table.hpp (Classic wire index 98)
- Add wire index 98 to Classic and Turtle WoW JSON update field tables
- Both Classic aura rebuild paths (CREATE_OBJECT and VALUES) now read the
  flag byte for each aura slot to populate AuraSlot.flags, enabling the
  buff/debuff bar to correctly separate buffs from debuffs on Classic
2026-03-12 21:33:19 -07:00
Kelsi
1bf4c2442a feat: add title selection window with CMSG_SET_TITLE support
Track player titles from SMSG_TITLE_EARNED into knownTitleBits_ set,
read active title from PLAYER_CHOSEN_TITLE update field (WotLK index
1349), expose via getFormattedTitle()/sendSetTitle() on GameHandler.

Add SetTitlePacket builder (CMSG_SET_TITLE: int32 titleBit, -1=clear).

Titles window (H key) lists all earned titles from CharTitles.dbc,
highlights the active one in gold, and lets the player click to equip
or unequip a title with a single server round-trip.
2026-03-12 20:23:36 -07:00
Kelsi
81b95b4af7 feat: resolve title names from CharTitles.dbc in SMSG_TITLE_EARNED
Previously SMSG_TITLE_EARNED only showed the numeric bit index.
Now it lazy-loads CharTitles.dbc and formats the full title string
with the player's name (e.g. "Title earned: Commander Kelsi!").

- Add CharTitles layout to WotLK (TitleBit=36) and TBC (TitleBit=20) layouts
- loadTitleNameCache() maps each titleBit to its English title string
- SMSG_TITLE_EARNED substitutes %s placeholder with local player's name
- Falls back to "Title earned (bit N)!" if DBC is unavailable
2026-03-12 19:05:54 -07:00
Kelsi
8921c2ddf4 feat: show criteria description and progress in achievement window
The Criteria tab now loads AchievementCriteria.dbc to display each
criterion's description text, parent achievement name, and a
current/required progress counter (e.g. "25/100") instead of the
raw numeric IDs. The search filter now also matches by achievement name.
AchievementCriteria DBC layout added to wotlk/dbc_layouts.json.
2026-03-12 12:52:08 -07:00
Kelsi
d44f5e6560 feat: show achievement description and point value in tooltip
Hovering an earned achievement now shows its point value (gold badge),
description text from Achievement.dbc field 21, and the earn date.
loadAchievementNameCache() also populates achievementDescCache_ and
achievementPointsCache_ in a single DBC pass; Points field (39) added
to the WotLK Achievement DBC layout.
2026-03-12 12:49:38 -07:00